[llvm] [BOLT] Add support for safe-icf (PR #116275)
Alexander Yermolovich via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 19 16:22:06 PST 2024
https://github.com/ayermolo updated https://github.com/llvm/llvm-project/pull/116275
>From 03ca42cce937fc7a85104e80fc271095e998b472 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Mon, 11 Nov 2024 19:19:02 -0800
Subject: [PATCH 1/6] safe-icf
Summary:
Test Plan:
Reviewers:
Subscribers:
Tasks:
Tags:
Differential Revision: https://phabricator.intern.facebook.com/D65799965
---
bolt/include/bolt/Core/BinaryContext.h | 9 +
bolt/include/bolt/Core/BinaryFunction.h | 9 +
.../bolt/Passes/IdenticalCodeFolding.h | 14 +-
bolt/lib/Core/BinaryContext.cpp | 86 +++++
bolt/lib/Core/BinaryFunction.cpp | 2 +
bolt/lib/Passes/IdenticalCodeFolding.cpp | 85 +++-
bolt/lib/Rewrite/BinaryPassManager.cpp | 15 +-
bolt/lib/Rewrite/BoltDiff.cpp | 3 +-
bolt/lib/Rewrite/RewriteInstance.cpp | 64 +--
bolt/test/X86/Inputs/helperSafeICF.s | 39 ++
bolt/test/X86/Inputs/helperSafeICFICPTest.s | 40 ++
bolt/test/X86/Inputs/mainSafeICFICPTest.s | 331 ++++++++++++++++
bolt/test/X86/Inputs/mainSafeICFTest1.s | 345 +++++++++++++++++
.../X86/Inputs/mainSafeICFTest2GlobalVarO0.s | 363 ++++++++++++++++++
.../X86/Inputs/mainSafeICFTest2GlobalVarO3.s | 293 ++++++++++++++
.../X86/Inputs/mainSafeICFTest3LocalVarO0.s | 359 +++++++++++++++++
.../X86/Inputs/mainSafeICFTest3LocalVarO3.s | 286 ++++++++++++++
bolt/test/X86/icf-safe-icp.test | 20 +
bolt/test/X86/icf-safe-test1-no-cfg.test | 21 +
bolt/test/X86/icf-safe-test1-no-relocs.test | 16 +
bolt/test/X86/icf-safe-test1.test | 21 +
bolt/test/X86/icf-safe-test2GlobalVarO0.test | 21 +
bolt/test/X86/icf-safe-test2GlobalVarO3.test | 21 +
bolt/test/X86/icf-safe-test3LocalVarO0.test | 21 +
bolt/test/X86/icf-safe-test3LocalVarO3.test | 21 +
25 files changed, 2431 insertions(+), 74 deletions(-)
create mode 100644 bolt/test/X86/Inputs/helperSafeICF.s
create mode 100644 bolt/test/X86/Inputs/helperSafeICFICPTest.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFICPTest.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest1.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s
create mode 100644 bolt/test/X86/icf-safe-icp.test
create mode 100644 bolt/test/X86/icf-safe-test1-no-cfg.test
create mode 100644 bolt/test/X86/icf-safe-test1-no-relocs.test
create mode 100644 bolt/test/X86/icf-safe-test1.test
create mode 100644 bolt/test/X86/icf-safe-test2GlobalVarO0.test
create mode 100644 bolt/test/X86/icf-safe-test2GlobalVarO3.test
create mode 100644 bolt/test/X86/icf-safe-test3LocalVarO0.test
create mode 100644 bolt/test/X86/icf-safe-test3LocalVarO3.test
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 08ce892054874c..92889ffe162ae1 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -282,6 +282,12 @@ class BinaryContext {
std::unique_ptr<DWARFContext> DwCtx,
JournalingStreams Logger);
+ /// Returns addend of a relocation.
+ static int64_t getRelocationAddend(const ELFObjectFileBase *Obj,
+ const RelocationRef &Rel);
+ /// Returns symbol of a relocation.
+ static uint32_t getRelocationSymbol(const ELFObjectFileBase *Obj,
+ const RelocationRef &Rel);
/// Superset of compiler units that will contain overwritten code that needs
/// new debug info. In a few cases, functions may end up not being
/// overwritten, but it is okay to re-generate debug info for them.
@@ -1350,6 +1356,9 @@ class BinaryContext {
return Code.size();
}
+ /// Processes .text section to identify function references.
+ void processInstructionForFuncReferences(const MCInst &Inst);
+
/// Compute the native code size for a range of instructions.
/// Note: this can be imprecise wrt the final binary since happening prior to
/// relaxation, as well as wrt the original binary because of opcode
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 0b3682353f736e..5e1ddcc6bff9a2 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -428,6 +428,9 @@ class BinaryFunction {
/// Function order for streaming into the destination binary.
uint32_t Index{-1U};
+ /// Indicates if the function is safe to fold.
+ bool IsSafeToICF{true};
+
/// Get basic block index assuming it belongs to this function.
unsigned getIndex(const BinaryBasicBlock *BB) const {
assert(BB->getIndex() < BasicBlocks.size());
@@ -817,6 +820,12 @@ class BinaryFunction {
return nullptr;
}
+ /// Indicates if the function is safe to fold.
+ bool isSafeToICF() const { return IsSafeToICF; }
+
+ /// Sets the function is not safe to fold.
+ void setUnsetToICF() { IsSafeToICF = false; }
+
/// Returns the raw binary encoding of this function.
ErrorOr<ArrayRef<uint8_t>> getData() const;
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index b4206fa3607445..75d5f19ddfc7be 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -31,11 +31,21 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
}
public:
- explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass)
- : BinaryFunctionPass(PrintPass) {}
+ explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass, bool IsSafeICF)
+ : BinaryFunctionPass(PrintPass), IsSafeICF(IsSafeICF) {}
const char *getName() const override { return "identical-code-folding"; }
Error runOnFunctions(BinaryContext &BC) override;
+
+private:
+ /// Create a skip list of functions that should not be folded.
+ Error createFoldSkipList(BinaryContext &BC);
+ /// Processes relocations in the .data section to identify function
+ /// references.
+ void processDataRelocations(BinaryContext &BC,
+ const SectionRef &SecRefRelData);
+
+ bool IsSafeICF;
};
} // namespace bolt
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index f246750209d6c4..31352bed6fdab7 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -75,8 +75,63 @@ cl::opt<std::string> CompDirOverride(
"location, which is used with DW_AT_dwo_name to construct a path "
"to *.dwo files."),
cl::Hidden, cl::init(""), cl::cat(BoltCategory));
+
+cl::opt<bool> ICF("icf", cl::desc("fold functions with identical code"),
+ cl::cat(BoltOptCategory));
+
+cl::opt<bool> SafeICF("safe-icf",
+ cl::desc("Enable safe identical code folding"),
+ cl::cat(BoltOptCategory));
} // namespace opts
+namespace {
+template <typename ELFT>
+int64_t getRelocationAddend(const ELFObjectFile<ELFT> *Obj,
+ const RelocationRef &RelRef) {
+ using ELFShdrTy = typename ELFT::Shdr;
+ using Elf_Rela = typename ELFT::Rela;
+ int64_t Addend = 0;
+ const ELFFile<ELFT> &EF = Obj->getELFFile();
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
+ const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
+ switch (RelocationSection->sh_type) {
+ default:
+ llvm_unreachable("unexpected relocation section type");
+ case ELF::SHT_REL:
+ break;
+ case ELF::SHT_RELA: {
+ const Elf_Rela *RelA = Obj->getRela(Rel);
+ Addend = RelA->r_addend;
+ break;
+ }
+ }
+
+ return Addend;
+}
+
+template <typename ELFT>
+uint32_t getRelocationSymbol(const ELFObjectFile<ELFT> *Obj,
+ const RelocationRef &RelRef) {
+ using ELFShdrTy = typename ELFT::Shdr;
+ uint32_t Symbol = 0;
+ const ELFFile<ELFT> &EF = Obj->getELFFile();
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
+ const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
+ switch (RelocationSection->sh_type) {
+ default:
+ llvm_unreachable("unexpected relocation section type");
+ case ELF::SHT_REL:
+ Symbol = Obj->getRel(Rel)->getSymbol(EF.isMips64EL());
+ break;
+ case ELF::SHT_RELA:
+ Symbol = Obj->getRela(Rel)->getSymbol(EF.isMips64EL());
+ break;
+ }
+
+ return Symbol;
+}
+} // anonymous namespace
+
namespace llvm {
namespace bolt {
@@ -156,6 +211,16 @@ BinaryContext::~BinaryContext() {
clearBinaryData();
}
+uint32_t BinaryContext::getRelocationSymbol(const ELFObjectFileBase *Obj,
+ const RelocationRef &Rel) {
+ return ::getRelocationSymbol(cast<ELF64LEObjectFile>(Obj), Rel);
+}
+
+int64_t BinaryContext::getRelocationAddend(const ELFObjectFileBase *Obj,
+ const RelocationRef &Rel) {
+ return ::getRelocationAddend(cast<ELF64LEObjectFile>(Obj), Rel);
+}
+
/// Create BinaryContext for a given architecture \p ArchName and
/// triple \p TripleName.
Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
@@ -1945,6 +2010,27 @@ static void printDebugInfo(raw_ostream &OS, const MCInst &Instruction,
OS << " discriminator:" << Row.Discriminator;
}
+static bool skipInstruction(const MCInst &Inst, const BinaryContext &BC) {
+ const bool IsX86 = BC.isX86();
+ return (BC.MIB->isPseudo(Inst) || BC.MIB->isUnconditionalBranch(Inst) ||
+ (IsX86 && BC.MIB->isConditionalBranch(Inst)) ||
+ BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst));
+}
+void BinaryContext::processInstructionForFuncReferences(const MCInst &Inst) {
+ if (!opts::SafeICF || skipInstruction(Inst, *this))
+ return;
+ for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
+ if (Op.isExpr()) {
+ const MCExpr &Expr = *Op.getExpr();
+ if (Expr.getKind() == MCExpr::SymbolRef) {
+ const MCSymbol &Symbol = cast<MCSymbolRefExpr>(Expr).getSymbol();
+ if (BinaryFunction *BF = getFunctionForSymbol(&Symbol))
+ BF->setUnsetToICF();
+ }
+ }
+ }
+}
+
void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction,
uint64_t Offset,
const BinaryFunction *Function,
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index c12217d549479b..07d386efb26ec5 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1626,6 +1626,8 @@ bool BinaryFunction::scanExternalRefs() {
if (!BC.HasRelocations)
continue;
+ BC.processInstructionForFuncReferences(Instruction);
+
if (BranchTargetSymbol) {
BC.MIB->replaceBranchTarget(Instruction, BranchTargetSymbol,
Emitter.LocalCtx.get());
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 38e080c9dd6213..f09482dc08c212 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -11,8 +11,10 @@
//===----------------------------------------------------------------------===//
#include "bolt/Passes/IdenticalCodeFolding.h"
+#include "bolt/Core/BinaryContext.h"
#include "bolt/Core/HashUtilities.h"
#include "bolt/Core/ParallelUtilities.h"
+#include "bolt/Rewrite/RewriteInstance.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ThreadPool.h"
@@ -31,6 +33,7 @@ using namespace bolt;
namespace opts {
extern cl::OptionCategory BoltOptCategory;
+extern cl::opt<unsigned> Verbosity;
static cl::opt<bool>
ICFUseDFS("icf-dfs", cl::desc("use DFS ordering when using -icf option"),
@@ -341,6 +344,75 @@ typedef std::unordered_map<BinaryFunction *, std::vector<BinaryFunction *>,
namespace llvm {
namespace bolt {
+void IdenticalCodeFolding::processDataRelocations(
+ BinaryContext &BC, const SectionRef &SecRefRelData) {
+ for (const RelocationRef &Rel : SecRefRelData.relocations()) {
+ symbol_iterator SymbolIter = Rel.getSymbol();
+ const ObjectFile *OwningObj = Rel.getObject();
+ assert(SymbolIter != OwningObj->symbol_end() &&
+ "relocation Symbol expected");
+ const SymbolRef &Symbol = *SymbolIter;
+ const uint64_t SymbolAddress = cantFail(Symbol.getAddress());
+ const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(OwningObj);
+ if (!ELFObj)
+ assert(false && "Only ELFObjectFileBase is supported");
+ const int64_t Addend = BinaryContext::getRelocationAddend(ELFObj, Rel);
+ BinaryFunction *BF = BC.getBinaryFunctionAtAddress(SymbolAddress + Addend);
+ if (!BF)
+ continue;
+ BF->setUnsetToICF();
+ }
+}
+
+Error IdenticalCodeFolding::createFoldSkipList(BinaryContext &BC) {
+ Error ErrorStatus = Error::success();
+ ErrorOr<BinarySection &> SecRelData = BC.getUniqueSectionByName(".rela.data");
+ if (!BC.HasRelocations)
+ ErrorStatus = joinErrors(
+ std::move(ErrorStatus),
+ createFatalBOLTError(Twine("BOLT-ERROR: Binary built without "
+ "relocations. Safe ICF is not supported")));
+ if (ErrorStatus)
+ return ErrorStatus;
+ if (SecRelData) {
+ SectionRef SecRefRelData = SecRelData->getSectionRef();
+ processDataRelocations(BC, SecRefRelData);
+ }
+
+ ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+ if (BF.getState() == BinaryFunction::State::CFG) {
+ for (const BinaryBasicBlock *BB : BF.getLayout().blocks())
+ for (const MCInst &Inst : *BB)
+ BC.processInstructionForFuncReferences(Inst);
+ }
+ };
+ ParallelUtilities::PredicateTy SkipFunc =
+ [&](const BinaryFunction &BF) -> bool { return (bool)ErrorStatus; };
+ ParallelUtilities::runOnEachFunction(
+ BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
+ "markUnsafe", /*ForceSequential*/ false, 2);
+
+ LLVM_DEBUG({
+ std::vector<StringRef> Vect;
+ std::mutex PrintMutex;
+ ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+ if (BF.isSafeToICF())
+ return;
+ std::lock_guard<std::mutex> Lock(PrintMutex);
+ Vect.push_back(BF.getOneName());
+ };
+ ParallelUtilities::PredicateTy SkipFunc =
+ [&](const BinaryFunction &BF) -> bool { return false; };
+ ParallelUtilities::runOnEachFunction(
+ BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
+ "markUnsafe", /*ForceSequential*/ false, 2);
+ llvm::sort(Vect);
+ for (const auto &FuncName : Vect)
+ dbgs() << "BOLT-DEBUG: skipping function " << FuncName << '\n';
+ });
+ return ErrorStatus;
+}
+
Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
const size_t OriginalFunctionCount = BC.getBinaryFunctions().size();
uint64_t NumFunctionsFolded = 0;
@@ -350,6 +422,9 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
std::atomic<uint64_t> NumFoldedLastIteration{0};
CongruentBucketsMap CongruentBuckets;
+ auto SkipFuncShared = [&](const BinaryFunction &BF) {
+ return !shouldOptimize(BF) || !BF.isSafeToICF();
+ };
// Hash all the functions
auto hashFunctions = [&]() {
NamedRegionTimer HashFunctionsTimer("hashing", "hashing", "ICF breakdown",
@@ -369,7 +444,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
};
ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
- return !shouldOptimize(BF);
+ return SkipFuncShared(BF);
};
ParallelUtilities::runOnEachFunction(
@@ -385,7 +460,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
"ICF breakdown", opts::TimeICF);
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &BF = BFI.second;
- if (!this->shouldOptimize(BF))
+ if (SkipFuncShared(BF))
continue;
CongruentBuckets[&BF].emplace(&BF);
}
@@ -475,7 +550,11 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
LLVM_DEBUG(SinglePass.stopTimer());
};
-
+ if (IsSafeICF) {
+ if (Error Err = createFoldSkipList(BC)) {
+ return Err;
+ }
+ }
hashFunctions();
createCongruentBuckets();
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index b0906041833484..df612c0a8f2e51 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -54,6 +54,8 @@ extern cl::opt<bool> PrintDynoStats;
extern cl::opt<bool> DumpDotAll;
extern cl::opt<std::string> AsmDump;
extern cl::opt<bolt::PLTCall::OptType> PLT;
+extern cl::opt<bool> ICF;
+extern cl::opt<bool> SafeICF;
static cl::opt<bool>
DynoStatsAll("dyno-stats-all",
@@ -65,9 +67,6 @@ static cl::opt<bool>
cl::desc("eliminate unreachable code"), cl::init(true),
cl::cat(BoltOptCategory));
-cl::opt<bool> ICF("icf", cl::desc("fold functions with identical code"),
- cl::cat(BoltOptCategory));
-
static cl::opt<bool> JTFootprintReductionFlag(
"jt-footprint-reduction",
cl::desc("make jump tables size smaller at the cost of using more "
@@ -397,8 +396,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<StripRepRet>(NeverPrint),
opts::StripRepRet);
- Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
- opts::ICF);
+ Manager.registerPass(
+ std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
+ opts::ICF || opts::SafeICF);
Manager.registerPass(
std::make_unique<SpecializeMemcpy1>(NeverPrint, opts::SpecializeMemcpy1),
@@ -422,8 +422,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<Inliner>(PrintInline));
- Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
- opts::ICF);
+ Manager.registerPass(
+ std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
+ opts::ICF || opts::SafeICF);
Manager.registerPass(std::make_unique<PLTCall>(PrintPLT));
diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp
index 74b5ca18abce42..1a8eb5cd2ce39d 100644
--- a/bolt/lib/Rewrite/BoltDiff.cpp
+++ b/bolt/lib/Rewrite/BoltDiff.cpp
@@ -29,6 +29,7 @@ namespace opts {
extern cl::OptionCategory BoltDiffCategory;
extern cl::opt<bool> NeverPrint;
extern cl::opt<bool> ICF;
+extern cl::opt<bool> SafeICF;
static cl::opt<bool> IgnoreLTOSuffix(
"ignore-lto-suffix",
@@ -698,7 +699,7 @@ void RewriteInstance::compare(RewriteInstance &RI2) {
// Pre-pass ICF
if (opts::ICF) {
- IdenticalCodeFolding ICF(opts::NeverPrint);
+ IdenticalCodeFolding ICF(opts::NeverPrint, opts::SafeICF);
outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
BC->logBOLTErrorsAndQuitOnFatal(ICF.runOnFunctions(*BC));
outs() << "BOLT-DIFF: Starting ICF pass for binary 2";
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index a4c50cbc3e2bbf..70c9eeef3cdc57 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2111,64 +2111,6 @@ void RewriteInstance::adjustCommandLineOptions() {
}
}
-namespace {
-template <typename ELFT>
-int64_t getRelocationAddend(const ELFObjectFile<ELFT> *Obj,
- const RelocationRef &RelRef) {
- using ELFShdrTy = typename ELFT::Shdr;
- using Elf_Rela = typename ELFT::Rela;
- int64_t Addend = 0;
- const ELFFile<ELFT> &EF = Obj->getELFFile();
- DataRefImpl Rel = RelRef.getRawDataRefImpl();
- const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
- switch (RelocationSection->sh_type) {
- default:
- llvm_unreachable("unexpected relocation section type");
- case ELF::SHT_REL:
- break;
- case ELF::SHT_RELA: {
- const Elf_Rela *RelA = Obj->getRela(Rel);
- Addend = RelA->r_addend;
- break;
- }
- }
-
- return Addend;
-}
-
-int64_t getRelocationAddend(const ELFObjectFileBase *Obj,
- const RelocationRef &Rel) {
- return getRelocationAddend(cast<ELF64LEObjectFile>(Obj), Rel);
-}
-
-template <typename ELFT>
-uint32_t getRelocationSymbol(const ELFObjectFile<ELFT> *Obj,
- const RelocationRef &RelRef) {
- using ELFShdrTy = typename ELFT::Shdr;
- uint32_t Symbol = 0;
- const ELFFile<ELFT> &EF = Obj->getELFFile();
- DataRefImpl Rel = RelRef.getRawDataRefImpl();
- const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
- switch (RelocationSection->sh_type) {
- default:
- llvm_unreachable("unexpected relocation section type");
- case ELF::SHT_REL:
- Symbol = Obj->getRel(Rel)->getSymbol(EF.isMips64EL());
- break;
- case ELF::SHT_RELA:
- Symbol = Obj->getRela(Rel)->getSymbol(EF.isMips64EL());
- break;
- }
-
- return Symbol;
-}
-
-uint32_t getRelocationSymbol(const ELFObjectFileBase *Obj,
- const RelocationRef &Rel) {
- return getRelocationSymbol(cast<ELF64LEObjectFile>(Obj), Rel);
-}
-} // anonymous namespace
-
bool RewriteInstance::analyzeRelocation(
const RelocationRef &Rel, uint64_t &RType, std::string &SymbolName,
bool &IsSectionRelocation, uint64_t &SymbolAddress, int64_t &Addend,
@@ -2196,7 +2138,7 @@ bool RewriteInstance::analyzeRelocation(
return true;
ExtractedValue = Relocation::extractValue(RType, *Value, Rel.getOffset());
- Addend = getRelocationAddend(InputFile, Rel);
+ Addend = BinaryContext::getRelocationAddend(InputFile, Rel);
const bool IsPCRelative = Relocation::isPCRelative(RType);
const uint64_t PCRelOffset = IsPCRelative && !IsAArch64 ? Rel.getOffset() : 0;
@@ -2396,7 +2338,7 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
StringRef SymbolName = "<none>";
MCSymbol *Symbol = nullptr;
uint64_t SymbolAddress = 0;
- const uint64_t Addend = getRelocationAddend(InputFile, Rel);
+ const uint64_t Addend = BinaryContext::getRelocationAddend(InputFile, Rel);
symbol_iterator SymbolIter = Rel.getSymbol();
if (SymbolIter != InputFile->symbol_end()) {
@@ -2421,7 +2363,7 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
IsJmpRelocation[RType] = true;
if (Symbol)
- SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
+ SymbolIndex[Symbol] = BinaryContext::getRelocationSymbol(InputFile, Rel);
BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend);
}
diff --git a/bolt/test/X86/Inputs/helperSafeICF.s b/bolt/test/X86/Inputs/helperSafeICF.s
new file mode 100644
index 00000000000000..8ab47324454cbc
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICF.s
@@ -0,0 +1,39 @@
+# clang++ -c helper.cpp -o helper.o
+# int FooVar = 1;
+# int BarVar = 2;
+#
+# int fooGlobalFuncHelper(int a, int b) {
+# return 5;
+# }
+ .text
+ .file "helper.cpp"
+ .globl _Z19fooGlobalFuncHelperii # -- Begin function _Z19fooGlobalFuncHelperii
+ .p2align 4, 0x90
+ .type _Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii: # @_Z19fooGlobalFuncHelperii
+ .cfi_startproc
+# %bb.0:
+ movl $5, %eax
+ retq
+.Lfunc_end0:
+ .size _Z19fooGlobalFuncHelperii, .Lfunc_end0-_Z19fooGlobalFuncHelperii
+ .cfi_endproc
+ # -- End function
+ .type FooVar, at object # @FooVar
+ .data
+ .globl FooVar
+ .p2align 2, 0x0
+FooVar:
+ .long 1 # 0x1
+ .size FooVar, 4
+
+ .type BarVar, at object # @BarVar
+ .globl BarVar
+ .p2align 2, 0x0
+BarVar:
+ .long 2 # 0x2
+ .size BarVar, 4
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
diff --git a/bolt/test/X86/Inputs/helperSafeICFICPTest.s b/bolt/test/X86/Inputs/helperSafeICFICPTest.s
new file mode 100644
index 00000000000000..ae62b4f292656f
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFICPTest.s
@@ -0,0 +1,40 @@
+# clang++ -O2 helper.cpp -c -o helperProf.o
+# int returnFive() {
+# return 5;
+# }
+# int returnFourOrFive(int val) {
+# return val == 1 ? 4 : 5;
+# }
+
+ .text
+ .file "helper.cpp"
+ .globl _Z10returnFivev # -- Begin function _Z10returnFivev
+ .p2align 4, 0x90
+ .type _Z10returnFivev, at function
+_Z10returnFivev: # @_Z10returnFivev
+ .cfi_startproc
+# %bb.0: # %entry
+ movl $5, %eax
+ retq
+.Lfunc_end0:
+ .size _Z10returnFivev, .Lfunc_end0-_Z10returnFivev
+ .cfi_endproc
+ # -- End function
+ .globl _Z16returnFourOrFivei # -- Begin function _Z16returnFourOrFivei
+ .p2align 4, 0x90
+ .type _Z16returnFourOrFivei, at function
+_Z16returnFourOrFivei: # @_Z16returnFourOrFivei
+ .cfi_startproc
+# %bb.0: # %entry
+ xorl %eax, %eax
+ cmpl $1, %edi
+ sete %al
+ xorl $5, %eax
+ retq
+.Lfunc_end1:
+ .size _Z16returnFourOrFivei, .Lfunc_end1-_Z16returnFourOrFivei
+ .cfi_endproc
+ # -- End function
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
diff --git a/bolt/test/X86/Inputs/mainSafeICFICPTest.s b/bolt/test/X86/Inputs/mainSafeICFICPTest.s
new file mode 100644
index 00000000000000..4b12acdeae0995
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFICPTest.s
@@ -0,0 +1,331 @@
+# generate profile
+# clang++ -O2 -fprofile-generate=. main.cpp -c -o mainProf.o
+# PROF=test.profdata
+# clang++ -m64 -fprofile-use=$PROF \
+# -mllvm -disable-icp=true -mllvm -print-after-all \
+# -g0 -flto=thin -fwhole-program-vtables -fno-split-lto-unit -O2 \
+# -fdebug-types-section \
+# main.cpp -c -o mainProfLTO.bc
+# PASS='pgo-icall-prom'
+# clang++ -m64 -fprofile-use=$PROF \
+# -O3 -Rpass=$PASS \
+# -mllvm -print-before=$PASS \
+# -mllvm -print-after=$PASS \
+# -mllvm -filter-print-funcs=main \
+# -mllvm -debug-only=$PASS \
+# -x ir \
+# mainProfLTO.bc -c -o mainProfFinal.o
+
+# class Base {
+# public:
+# virtual int func(int a, int b) const = 0;
+#
+# virtual ~Base() {};
+# };
+#
+# //namespace {
+# class Derived2 : public Base {
+# int c = 5;
+# public:
+# __attribute__((noinline)) int func(int a, int b)const override { return a * (a - b) + this->c; }
+#
+# ~Derived2() {}
+# };
+#
+# class Derived3 : public Base {
+# int c = 500;
+# public:
+# __attribute__((noinline)) int func(int a, int b) const override { return a * (a - b) + this->c; }
+# ~Derived3() {}
+# };
+# //} // namespace//
+#
+# __attribute__((noinline)) Base *createType(int a) {
+# Base *base = nullptr;
+# if (a == 4)
+# base = new Derived2();
+# else
+# base = new Derived3();
+# return base;
+# }
+#
+# extern int returnFive();
+# extern int returnFourOrFive(int val);
+# int main(int argc, char **argv) {
+# int sum = 0;
+# int a = returnFourOrFive(argc);
+# int b = returnFive();
+# Base *ptr = createType(a);
+# Base *ptr2 = createType(b);
+# sum += ptr->func(b, a) + ptr2->func(b, a);
+# return 0;
+# }
+ .text
+ .file "main.cpp"
+ .section .text.hot.,"ax", at progbits
+ .globl _Z10createTypei # -- Begin function _Z10createTypei
+ .p2align 4, 0x90
+ .type _Z10createTypei, at function
+_Z10createTypei: # @_Z10createTypei
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbx
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbx, -16
+ movl %edi, %ebx
+ movl $16, %edi
+ callq _Znwm at PLT
+ cmpl $4, %ebx
+ xorps %xmm0, %xmm0
+ leaq _ZTV8Derived2+16(%rip), %rcx
+ leaq _ZTV8Derived3+16(%rip), %rdx
+ cmoveq %rcx, %rdx
+ movl $5, %ecx
+ movl $500, %esi # imm = 0x1F4
+ cmovel %ecx, %esi
+ movaps %xmm0, (%rax)
+ movq %rdx, (%rax)
+ movl %esi, 8(%rax)
+ popq %rbx
+ .cfi_def_cfa_offset 8
+ retq
+.Lfunc_end0:
+ .size _Z10createTypei, .Lfunc_end0-_Z10createTypei
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ pushq %r15
+ .cfi_def_cfa_offset 24
+ pushq %r14
+ .cfi_def_cfa_offset 32
+ pushq %rbx
+ .cfi_def_cfa_offset 40
+ pushq %rax
+ .cfi_def_cfa_offset 48
+ .cfi_offset %rbx, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ .cfi_offset %rbp, -16
+ callq _Z16returnFourOrFivei at PLT
+ movl %eax, %ebx
+ callq _Z10returnFivev at PLT
+ movl %eax, %ebp
+ movl %ebx, %edi
+ callq _Z10createTypei
+ movq %rax, %r15
+ movl %ebp, %edi
+ callq _Z10createTypei
+ movq %rax, %r14
+ movq (%r15), %rax
+ movq (%rax), %rax
+ leaq _ZNK8Derived24funcEii(%rip), %rcx
+ movq %r15, %rdi
+ movl %ebp, %esi
+ movl %ebx, %edx
+ cmpq %rcx, %rax
+ jne .LBB1_2
+# %bb.1: # %if.true.direct_targ
+ callq _ZNK8Derived24funcEii
+.LBB1_3: # %if.end.icp
+ movq (%r14), %rax
+ movq (%rax), %rax
+ leaq _ZNK8Derived34funcEii(%rip), %rcx
+ movq %r14, %rdi
+ movl %ebp, %esi
+ movl %ebx, %edx
+ cmpq %rcx, %rax
+ jne .LBB1_5
+# %bb.4: # %if.true.direct_targ1
+ callq _ZNK8Derived34funcEii
+.LBB1_6: # %if.end.icp3
+ xorl %eax, %eax
+ addq $8, %rsp
+ .cfi_def_cfa_offset 40
+ popq %rbx
+ .cfi_def_cfa_offset 32
+ popq %r14
+ .cfi_def_cfa_offset 24
+ popq %r15
+ .cfi_def_cfa_offset 16
+ popq %rbp
+ .cfi_def_cfa_offset 8
+ retq
+.LBB1_2: # %if.false.orig_indirect
+ .cfi_def_cfa_offset 48
+ callq *%rax
+ jmp .LBB1_3
+.LBB1_5: # %if.false.orig_indirect2
+ callq *%rax
+ jmp .LBB1_6
+.Lfunc_end1:
+ .size main, .Lfunc_end1-main
+ .cfi_endproc
+ # -- End function
+ .section .text.hot._ZNK8Derived24funcEii,"axG", at progbits,_ZNK8Derived24funcEii,comdat
+ .weak _ZNK8Derived24funcEii # -- Begin function _ZNK8Derived24funcEii
+ .p2align 4, 0x90
+ .type _ZNK8Derived24funcEii, at function
+_ZNK8Derived24funcEii: # @_ZNK8Derived24funcEii
+ .cfi_startproc
+# %bb.0: # %entry
+ movl %esi, %eax
+ subl %edx, %eax
+ imull %esi, %eax
+ addl 8(%rdi), %eax
+ retq
+.Lfunc_end2:
+ .size _ZNK8Derived24funcEii, .Lfunc_end2-_ZNK8Derived24funcEii
+ .cfi_endproc
+ # -- End function
+ .section .text.unlikely._ZN8Derived2D0Ev,"axG", at progbits,_ZN8Derived2D0Ev,comdat
+ .weak _ZN8Derived2D0Ev # -- Begin function _ZN8Derived2D0Ev
+ .p2align 4, 0x90
+ .type _ZN8Derived2D0Ev, at function
+_ZN8Derived2D0Ev: # @_ZN8Derived2D0Ev
+ .cfi_startproc
+# %bb.0: # %entry
+ movl $16, %esi
+ jmp _ZdlPvm at PLT # TAILCALL
+.Lfunc_end3:
+ .size _ZN8Derived2D0Ev, .Lfunc_end3-_ZN8Derived2D0Ev
+ .cfi_endproc
+ # -- End function
+ .section .text.hot._ZNK8Derived34funcEii,"axG", at progbits,_ZNK8Derived34funcEii,comdat
+ .weak _ZNK8Derived34funcEii # -- Begin function _ZNK8Derived34funcEii
+ .p2align 4, 0x90
+ .type _ZNK8Derived34funcEii, at function
+_ZNK8Derived34funcEii: # @_ZNK8Derived34funcEii
+ .cfi_startproc
+# %bb.0: # %entry
+ movl %esi, %eax
+ subl %edx, %eax
+ imull %esi, %eax
+ addl 8(%rdi), %eax
+ retq
+.Lfunc_end4:
+ .size _ZNK8Derived34funcEii, .Lfunc_end4-_ZNK8Derived34funcEii
+ .cfi_endproc
+ # -- End function
+ .section .text.unlikely._ZN4BaseD2Ev,"axG", at progbits,_ZN4BaseD2Ev,comdat
+ .weak _ZN4BaseD2Ev # -- Begin function _ZN4BaseD2Ev
+ .p2align 4, 0x90
+ .type _ZN4BaseD2Ev, at function
+_ZN4BaseD2Ev: # @_ZN4BaseD2Ev
+ .cfi_startproc
+# %bb.0: # %entry
+ retq
+.Lfunc_end5:
+ .size _ZN4BaseD2Ev, .Lfunc_end5-_ZN4BaseD2Ev
+ .cfi_endproc
+ # -- End function
+ .section .text.unlikely._ZN8Derived3D0Ev,"axG", at progbits,_ZN8Derived3D0Ev,comdat
+ .weak _ZN8Derived3D0Ev # -- Begin function _ZN8Derived3D0Ev
+ .p2align 4, 0x90
+ .type _ZN8Derived3D0Ev, at function
+_ZN8Derived3D0Ev: # @_ZN8Derived3D0Ev
+ .cfi_startproc
+# %bb.0: # %entry
+ movl $16, %esi
+ jmp _ZdlPvm at PLT # TAILCALL
+.Lfunc_end6:
+ .size _ZN8Derived3D0Ev, .Lfunc_end6-_ZN8Derived3D0Ev
+ .cfi_endproc
+ # -- End function
+ .type _ZTV8Derived2, at object # @_ZTV8Derived2
+ .section .data.rel.ro._ZTV8Derived2,"awG", at progbits,_ZTV8Derived2,comdat
+ .weak _ZTV8Derived2
+ .p2align 3, 0x0
+_ZTV8Derived2:
+ .quad 0
+ .quad _ZTI8Derived2
+ .quad _ZNK8Derived24funcEii
+ .quad _ZN4BaseD2Ev
+ .quad _ZN8Derived2D0Ev
+ .size _ZTV8Derived2, 40
+
+ .type _ZTS8Derived2, at object # @_ZTS8Derived2
+ .section .rodata._ZTS8Derived2,"aG", at progbits,_ZTS8Derived2,comdat
+ .weak _ZTS8Derived2
+_ZTS8Derived2:
+ .asciz "8Derived2"
+ .size _ZTS8Derived2, 10
+
+ .type _ZTS4Base, at object # @_ZTS4Base
+ .section .rodata._ZTS4Base,"aG", at progbits,_ZTS4Base,comdat
+ .weak _ZTS4Base
+_ZTS4Base:
+ .asciz "4Base"
+ .size _ZTS4Base, 6
+
+ .type _ZTI4Base, at object # @_ZTI4Base
+ .section .data.rel.ro._ZTI4Base,"awG", at progbits,_ZTI4Base,comdat
+ .weak _ZTI4Base
+ .p2align 3, 0x0
+_ZTI4Base:
+ .quad _ZTVN10__cxxabiv117__class_type_infoE+16
+ .quad _ZTS4Base
+ .size _ZTI4Base, 16
+
+ .type _ZTI8Derived2, at object # @_ZTI8Derived2
+ .section .data.rel.ro._ZTI8Derived2,"awG", at progbits,_ZTI8Derived2,comdat
+ .weak _ZTI8Derived2
+ .p2align 3, 0x0
+_ZTI8Derived2:
+ .quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
+ .quad _ZTS8Derived2
+ .quad _ZTI4Base
+ .size _ZTI8Derived2, 24
+
+ .type _ZTV8Derived3, at object # @_ZTV8Derived3
+ .section .data.rel.ro._ZTV8Derived3,"awG", at progbits,_ZTV8Derived3,comdat
+ .weak _ZTV8Derived3
+ .p2align 3, 0x0
+_ZTV8Derived3:
+ .quad 0
+ .quad _ZTI8Derived3
+ .quad _ZNK8Derived34funcEii
+ .quad _ZN4BaseD2Ev
+ .quad _ZN8Derived3D0Ev
+ .size _ZTV8Derived3, 40
+
+ .type _ZTS8Derived3, at object # @_ZTS8Derived3
+ .section .rodata._ZTS8Derived3,"aG", at progbits,_ZTS8Derived3,comdat
+ .weak _ZTS8Derived3
+_ZTS8Derived3:
+ .asciz "8Derived3"
+ .size _ZTS8Derived3, 10
+
+ .type _ZTI8Derived3, at object # @_ZTI8Derived3
+ .section .data.rel.ro._ZTI8Derived3,"awG", at progbits,_ZTI8Derived3,comdat
+ .weak _ZTI8Derived3
+ .p2align 3, 0x0
+_ZTI8Derived3:
+ .quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
+ .quad _ZTS8Derived3
+ .quad _ZTI4Base
+ .size _ZTI8Derived3, 24
+
+ .cg_profile _Z10createTypei, _Znwm, 2
+ .cg_profile main, _Z16returnFourOrFivei, 1
+ .cg_profile main, _Z10returnFivev, 1
+ .cg_profile main, _Z10createTypei, 2
+ .cg_profile main, _ZNK8Derived24funcEii, 1
+ .cg_profile main, _ZNK8Derived34funcEii, 1
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _ZTVN10__cxxabiv120__si_class_type_infoE
+ .addrsig_sym _ZTS8Derived2
+ .addrsig_sym _ZTVN10__cxxabiv117__class_type_infoE
+ .addrsig_sym _ZTS4Base
+ .addrsig_sym _ZTI4Base
+ .addrsig_sym _ZTI8Derived2
+ .addrsig_sym _ZTS8Derived3
+ .addrsig_sym _ZTI8Derived3
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest1.s b/bolt/test/X86/Inputs/mainSafeICFTest1.s
new file mode 100644
index 00000000000000..7d38f0bd190fd8
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest1.s
@@ -0,0 +1,345 @@
+# clang++ -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# int main(int argc, char **argv) {
+# int temp = helper1(barAdd, FooVar, BarVar) +
+# helper2(fooMul, barMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# return temp;
+# }
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFiiiEii # -- Begin function _Z7helper1PFiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii: # @_Z7helper1PFiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ leaq _Z6barAddii(%rip), %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFiiiES0_ii # -- Begin function _Z7helper2PFiiiES0_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii: # @_Z7helper2PFiiiES0_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ leaq _Z6barAddii(%rip), %rdi
+ callq _Z7helper1PFiiiEii
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ leaq _Z6barMulii(%rip), %rsi
+ callq _Z7helper2PFiiiES0_ii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -28(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -24(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -24(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -20(%rbp)
+ movl -20(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFiiiEii
+ .addrsig_sym _Z7helper2PFiiiES0_ii
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s
new file mode 100644
index 00000000000000..5d5d00c12320be
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s
@@ -0,0 +1,363 @@
+# clang++ -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# static int (*funcGlobalBarAdd)(int, int) = barAdd;
+# int (*funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# return temp;
+# }
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFiiiEii # -- Begin function _Z7helper1PFiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii: # @_Z7helper1PFiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ leaq _Z6barAddii(%rip), %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFiiiES0_ii # -- Begin function _Z7helper2PFiiiES0_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii: # @_Z7helper2PFiiiES0_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ movq _ZL16funcGlobalBarAdd(%rip), %rdi
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ callq _Z7helper1PFiiiEii
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq funcGlobalBarMul(%rip), %rsi
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ callq _Z7helper2PFiiiES0_ii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -28(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -24(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -24(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -20(%rbp)
+ movl -20(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .type funcGlobalBarMul, at object # @funcGlobalBarMul
+ .data
+ .globl funcGlobalBarMul
+ .p2align 3, 0x0
+funcGlobalBarMul:
+ .quad _Z6barMulii
+ .size funcGlobalBarMul, 8
+
+ .type _ZL16funcGlobalBarAdd, at object # @_ZL16funcGlobalBarAdd
+ .p2align 3, 0x0
+_ZL16funcGlobalBarAdd:
+ .quad _Z6barAddii
+ .size _ZL16funcGlobalBarAdd, 8
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFiiiEii
+ .addrsig_sym _Z7helper2PFiiiES0_ii
+ .addrsig_sym funcGlobalBarMul
+ .addrsig_sym _ZL16funcGlobalBarAdd
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s
new file mode 100644
index 00000000000000..3579fa2eae293b
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s
@@ -0,0 +1,293 @@
+# clang++ -O3 -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# static int (*funcGlobalBarAdd)(int, int) = barAdd;
+# int (*funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# return temp;
+# }
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ subl %esi, %eax
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ subl %esi, %eax
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ imull %esi, %eax
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ imull %esi, %eax
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ # kill: def $esi killed $esi def $rsi
+ # kill: def $edi killed $edi def $rdi
+ leal (%rdi,%rsi), %eax
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ # kill: def $esi killed $esi def $rsi
+ # kill: def $edi killed $edi def $rdi
+ leal (%rdi,%rsi), %eax
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFiiiEii # -- Begin function _Z7helper1PFiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii: # @_Z7helper1PFiiiEii
+ .cfi_startproc
+# %bb.0:
+ leaq _Z6barAddii(%rip), %rcx
+ cmpq %rcx, %rdi
+ je .LBB6_1
+# %bb.2:
+ pushq %rax
+ .cfi_def_cfa_offset 16
+ movq %rdi, %rax
+ movl %esi, %edi
+ movl %edx, %esi
+ callq *%rax
+ addl $-4, %eax
+ addq $8, %rsp
+ .cfi_def_cfa_offset 8
+ retq
+.LBB6_1:
+ movl $1, %eax
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFiiiES0_ii # -- Begin function _Z7helper2PFiiiES0_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii: # @_Z7helper2PFiiiES0_ii
+ .cfi_startproc
+# %bb.0:
+ cmpq %rsi, %rdi
+ je .LBB7_1
+# %bb.2:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ pushq %r15
+ .cfi_def_cfa_offset 24
+ pushq %r14
+ .cfi_def_cfa_offset 32
+ pushq %rbx
+ .cfi_def_cfa_offset 40
+ pushq %rax
+ .cfi_def_cfa_offset 48
+ .cfi_offset %rbx, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ .cfi_offset %rbp, -16
+ movl %ecx, %ebx
+ movl %edx, %ebp
+ movq %rsi, %r14
+ movq %rdi, %rax
+ movl %edx, %edi
+ movl %ecx, %esi
+ callq *%rax
+ movl %eax, %r15d
+ movl %ebp, %edi
+ movl %ebx, %esi
+ callq *%r14
+ addl %r15d, %eax
+ addq $8, %rsp
+ .cfi_def_cfa_offset 40
+ popq %rbx
+ .cfi_def_cfa_offset 32
+ popq %r14
+ .cfi_def_cfa_offset 24
+ popq %r15
+ .cfi_def_cfa_offset 16
+ popq %rbp
+ .cfi_def_cfa_offset 8
+ .cfi_restore %rbx
+ .cfi_restore %r14
+ .cfi_restore %r15
+ .cfi_restore %rbp
+ retq
+.LBB7_1:
+ movl $2, %eax
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ pushq %r15
+ .cfi_def_cfa_offset 24
+ pushq %r14
+ .cfi_def_cfa_offset 32
+ pushq %r12
+ .cfi_def_cfa_offset 40
+ pushq %rbx
+ .cfi_def_cfa_offset 48
+ .cfi_offset %rbx, -48
+ .cfi_offset %r12, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ .cfi_offset %rbp, -16
+ movq FooVar at GOTPCREL(%rip), %r14
+ movl (%r14), %esi
+ movq BarVar at GOTPCREL(%rip), %r15
+ movl (%r15), %edx
+ leaq _Z6barAddii(%rip), %rdi
+ callq _Z7helper1PFiiiEii
+ movl %eax, %ebx
+ movq funcGlobalBarMul(%rip), %rsi
+ movl (%r14), %edx
+ movl (%r15), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ callq _Z7helper2PFiiiES0_ii
+ movl %eax, %ebp
+ addl %ebx, %ebp
+ movl (%r14), %ebx
+ movl (%r15), %r14d
+ movl %ebx, %edi
+ movl %r14d, %esi
+ callq _Z6fooSubii
+ movl %eax, %r15d
+ movl %ebx, %edi
+ movl %r14d, %esi
+ callq _Z6barSubii
+ movl %eax, %r12d
+ addl %r15d, %r12d
+ addl %ebp, %r12d
+ movl %ebx, %edi
+ movl %r14d, %esi
+ callq _Z6fooAddii
+ addl %r12d, %eax
+ popq %rbx
+ .cfi_def_cfa_offset 40
+ popq %r12
+ .cfi_def_cfa_offset 32
+ popq %r14
+ .cfi_def_cfa_offset 24
+ popq %r15
+ .cfi_def_cfa_offset 16
+ popq %rbp
+ .cfi_def_cfa_offset 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .type funcGlobalBarMul, at object # @funcGlobalBarMul
+ .data
+ .globl funcGlobalBarMul
+ .p2align 3, 0x0
+funcGlobalBarMul:
+ .quad _Z6barMulii
+ .size funcGlobalBarMul, 8
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6barAddii
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s
new file mode 100644
index 00000000000000..56036d1b14e2b9
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s
@@ -0,0 +1,359 @@
+
+# clang++ -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# int main(int argc, char **argv) {
+# static int (*funcGlobalBarAdd)(int, int) = barAdd;
+# int (*funcGlobalBarMul)(int, int) = barMul;
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# MY_PRINTF("val: %d", temp);
+# return temp;
+# }
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFiiiEii # -- Begin function _Z7helper1PFiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii: # @_Z7helper1PFiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ leaq _Z6barAddii(%rip), %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFiiiES0_ii # -- Begin function _Z7helper2PFiiiES0_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii: # @_Z7helper2PFiiiES0_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ leaq _Z6barMulii(%rip), %rax
+ movq %rax, -24(%rbp)
+ movq _ZZ4mainE16funcGlobalBarAdd(%rip), %rdi
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ callq _Z7helper1PFiiiEii
+ movl %eax, -44(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rsi
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ callq _Z7helper2PFiiiES0_ii
+ movl %eax, %ecx
+ movl -44(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -40(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -40(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp)
+ movl -28(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .type _ZZ4mainE16funcGlobalBarAdd, at object # @_ZZ4mainE16funcGlobalBarAdd
+ .data
+ .p2align 3, 0x0
+_ZZ4mainE16funcGlobalBarAdd:
+ .quad _Z6barAddii
+ .size _ZZ4mainE16funcGlobalBarAdd, 8
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFiiiEii
+ .addrsig_sym _Z7helper2PFiiiES0_ii
+ .addrsig_sym _ZZ4mainE16funcGlobalBarAdd
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s
new file mode 100644
index 00000000000000..aedad906912654
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s
@@ -0,0 +1,286 @@
+# clang++ -O3 -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# int main(int argc, char **argv) {
+# static int (*funcGlobalBarAdd)(int, int) = barAdd;
+# int (*funcGlobalBarMul)(int, int) = barMul;
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# MY_PRINTF("val: %d", temp);
+# return temp;
+# }
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ subl %esi, %eax
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ subl %esi, %eax
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ imull %esi, %eax
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ movl %edi, %eax
+ imull %esi, %eax
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ # kill: def $esi killed $esi def $rsi
+ # kill: def $edi killed $edi def $rdi
+ leal (%rdi,%rsi), %eax
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ # kill: def $esi killed $esi def $rsi
+ # kill: def $edi killed $edi def $rdi
+ leal (%rdi,%rsi), %eax
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFiiiEii # -- Begin function _Z7helper1PFiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii: # @_Z7helper1PFiiiEii
+ .cfi_startproc
+# %bb.0:
+ leaq _Z6barAddii(%rip), %rcx
+ cmpq %rcx, %rdi
+ je .LBB6_1
+# %bb.2:
+ pushq %rax
+ .cfi_def_cfa_offset 16
+ movq %rdi, %rax
+ movl %esi, %edi
+ movl %edx, %esi
+ callq *%rax
+ addl $-4, %eax
+ addq $8, %rsp
+ .cfi_def_cfa_offset 8
+ retq
+.LBB6_1:
+ movl $1, %eax
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFiiiES0_ii # -- Begin function _Z7helper2PFiiiES0_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii: # @_Z7helper2PFiiiES0_ii
+ .cfi_startproc
+# %bb.0:
+ cmpq %rsi, %rdi
+ je .LBB7_1
+# %bb.2:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ pushq %r15
+ .cfi_def_cfa_offset 24
+ pushq %r14
+ .cfi_def_cfa_offset 32
+ pushq %rbx
+ .cfi_def_cfa_offset 40
+ pushq %rax
+ .cfi_def_cfa_offset 48
+ .cfi_offset %rbx, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ .cfi_offset %rbp, -16
+ movl %ecx, %ebx
+ movl %edx, %ebp
+ movq %rsi, %r14
+ movq %rdi, %rax
+ movl %edx, %edi
+ movl %ecx, %esi
+ callq *%rax
+ movl %eax, %r15d
+ movl %ebp, %edi
+ movl %ebx, %esi
+ callq *%r14
+ addl %r15d, %eax
+ addq $8, %rsp
+ .cfi_def_cfa_offset 40
+ popq %rbx
+ .cfi_def_cfa_offset 32
+ popq %r14
+ .cfi_def_cfa_offset 24
+ popq %r15
+ .cfi_def_cfa_offset 16
+ popq %rbp
+ .cfi_def_cfa_offset 8
+ .cfi_restore %rbx
+ .cfi_restore %r14
+ .cfi_restore %r15
+ .cfi_restore %rbp
+ retq
+.LBB7_1:
+ movl $2, %eax
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ pushq %r15
+ .cfi_def_cfa_offset 24
+ pushq %r14
+ .cfi_def_cfa_offset 32
+ pushq %r12
+ .cfi_def_cfa_offset 40
+ pushq %rbx
+ .cfi_def_cfa_offset 48
+ .cfi_offset %rbx, -48
+ .cfi_offset %r12, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ .cfi_offset %rbp, -16
+ movq FooVar at GOTPCREL(%rip), %r14
+ movl (%r14), %esi
+ movq BarVar at GOTPCREL(%rip), %r15
+ movl (%r15), %edx
+ leaq _Z6barAddii(%rip), %rdi
+ callq _Z7helper1PFiiiEii
+ movl %eax, %ebx
+ movl (%r14), %edx
+ movl (%r15), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ leaq _Z6barMulii(%rip), %rsi
+ callq _Z7helper2PFiiiES0_ii
+ movl %eax, %ebp
+ addl %ebx, %ebp
+ movl (%r14), %ebx
+ movl (%r15), %r14d
+ movl %ebx, %edi
+ movl %r14d, %esi
+ callq _Z6fooSubii
+ movl %eax, %r15d
+ movl %ebx, %edi
+ movl %r14d, %esi
+ callq _Z6barSubii
+ movl %eax, %r12d
+ addl %r15d, %r12d
+ addl %ebp, %r12d
+ movl %ebx, %edi
+ movl %r14d, %esi
+ callq _Z6fooAddii
+ addl %r12d, %eax
+ popq %rbx
+ .cfi_def_cfa_offset 40
+ popq %r12
+ .cfi_def_cfa_offset 32
+ popq %r14
+ .cfi_def_cfa_offset 24
+ popq %r15
+ .cfi_def_cfa_offset 16
+ popq %rbp
+ .cfi_def_cfa_offset 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6barAddii
diff --git a/bolt/test/X86/icf-safe-icp.test b/bolt/test/X86/icf-safe-icp.test
new file mode 100644
index 00000000000000..efeaf469eedeea
--- /dev/null
+++ b/bolt/test/X86/icf-safe-icp.test
@@ -0,0 +1,20 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+# The compare is generated by the ICP path with instrumentation profiling.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFICPTest.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFICPTest.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _ZN8Derived3D0Ev into _ZN8Derived2D0Ev
+# ICFCHECK-NEXT: folding _ZNK8Derived34funcEii into _ZNK8Derived24funcEii
+
+# SAFEICFCHECK: skipping function _ZNK8Derived24funcEii
+# SAFEICFCHECK-NEXT: skipping function _ZNK8Derived34funcEii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _ZN8Derived3D0Ev into _ZN8Derived2D0Ev
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test1-no-cfg.test b/bolt/test/X86/icf-safe-test1-no-cfg.test
new file mode 100644
index 00000000000000..37c5debe8c814b
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1-no-cfg.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf --skip-funcs=_Z7helper1PFiiiEii,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test1-no-relocs.test b/bolt/test/X86/icf-safe-test1-no-relocs.test
new file mode 100644
index 00000000000000..3b5d7400d85115
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1-no-relocs.test
@@ -0,0 +1,16 @@
+## Checks that BOLT handles correctly with no relocations with --safe-icf option.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: not llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: BOLT-ERROR: Binary built without relocations. Safe ICF is not supported
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
new file mode 100644
index 00000000000000..2b41633d07bf93
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO0.test b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
new file mode 100644
index 00000000000000..904d792e225b9c
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest2GlobalVarO0.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO3.test b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
new file mode 100644
index 00000000000000..288faf252ad745
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest2GlobalVarO3.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO0.test b/bolt/test/X86/icf-safe-test3LocalVarO0.test
new file mode 100644
index 00000000000000..56a329b2f7a598
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test3LocalVarO0.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest3LocalVarO0.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO3.test b/bolt/test/X86/icf-safe-test3LocalVarO3.test
new file mode 100644
index 00000000000000..d0782e46b17e06
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test3LocalVarO3.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest3LocalVarO3.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
>From 237c02e48825e2e42ec7cee62ef4a83ad0a8c4b1 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Thu, 14 Nov 2024 17:38:10 -0800
Subject: [PATCH 2/6] Changed icf option
---
bolt/include/bolt/Core/BinaryContext.h | 11 ++++++++++
.../bolt/Passes/IdenticalCodeFolding.h | 6 ++----
bolt/lib/Core/BinaryContext.cpp | 21 ++++++++++++-------
bolt/lib/Passes/IdenticalCodeFolding.cpp | 2 +-
bolt/lib/Rewrite/BinaryPassManager.cpp | 12 ++++-------
bolt/lib/Rewrite/BoltDiff.cpp | 4 ++--
bolt/test/X86/icf-safe-icp.test | 2 +-
bolt/test/X86/icf-safe-test1-no-cfg.test | 4 ++--
bolt/test/X86/icf-safe-test1-no-relocs.test | 4 ++--
bolt/test/X86/icf-safe-test1.test | 2 +-
bolt/test/X86/icf-safe-test2GlobalVarO0.test | 2 +-
bolt/test/X86/icf-safe-test2GlobalVarO3.test | 2 +-
bolt/test/X86/icf-safe-test3LocalVarO0.test | 2 +-
bolt/test/X86/icf-safe-test3LocalVarO3.test | 2 +-
14 files changed, 44 insertions(+), 32 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 92889ffe162ae1..3e58ec502acd09 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -276,6 +276,12 @@ class BinaryContext {
void deregisterSectionName(const BinarySection &Section);
public:
+ enum class ICFLevel {
+ None,
+ Safe, // Safe ICF for all sections.
+ All, // Aggressive ICF for code, but safe ICF for data, similar to MSVC's
+ // behavior.
+ };
static Expected<std::unique_ptr<BinaryContext>>
createBinaryContext(Triple TheTriple, StringRef InputFileName,
SubtargetFeatures *Features, bool IsPIC,
@@ -666,6 +672,9 @@ class BinaryContext {
std::unique_ptr<MCAsmBackend> MAB;
+ /// ICF level to use for this binary.
+ ICFLevel ICFLevelVar{ICFLevel::None};
+
/// Allows BOLT to print to log whenever it is necessary (with or without
/// const references)
mutable JournalingStreams Logger;
@@ -1485,6 +1494,8 @@ class BinaryContext {
return *IOAddressMap;
}
+ ICFLevel getICFLevel() const { return ICFLevelVar; }
+
raw_ostream &outs() const { return Logger.Out; }
raw_ostream &errs() const { return Logger.Err; }
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index 75d5f19ddfc7be..41fb17408d5876 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -31,8 +31,8 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
}
public:
- explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass, bool IsSafeICF)
- : BinaryFunctionPass(PrintPass), IsSafeICF(IsSafeICF) {}
+ explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass)
+ : BinaryFunctionPass(PrintPass) {}
const char *getName() const override { return "identical-code-folding"; }
Error runOnFunctions(BinaryContext &BC) override;
@@ -44,8 +44,6 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
/// references.
void processDataRelocations(BinaryContext &BC,
const SectionRef &SecRefRelData);
-
- bool IsSafeICF;
};
} // namespace bolt
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 31352bed6fdab7..6cf1562735eb45 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -76,12 +76,8 @@ cl::opt<std::string> CompDirOverride(
"to *.dwo files."),
cl::Hidden, cl::init(""), cl::cat(BoltCategory));
-cl::opt<bool> ICF("icf", cl::desc("fold functions with identical code"),
- cl::cat(BoltOptCategory));
-
-cl::opt<bool> SafeICF("safe-icf",
- cl::desc("Enable safe identical code folding"),
- cl::cat(BoltOptCategory));
+cl::opt<std::string> ICF("icf", cl::desc("fold functions with identical code"),
+ cl::ValueOptional, cl::cat(BoltOptCategory));
} // namespace opts
namespace {
@@ -175,6 +171,16 @@ void BinaryContext::logBOLTErrorsAndQuitOnFatal(Error E) {
});
}
+static BinaryContext::ICFLevel parseICFLevel() {
+ if (!opts::ICF.getNumOccurrences())
+ return BinaryContext::ICFLevel::None;
+ std::string Str = StringRef(opts::ICF).lower();
+ return StringSwitch<BinaryContext::ICFLevel>(Str)
+ .Case("all", BinaryContext::ICFLevel::All)
+ .Case("safe", BinaryContext::ICFLevel::Safe)
+ .Default(BinaryContext::ICFLevel::All);
+}
+
BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
std::unique_ptr<DWARFContext> DwCtx,
std::unique_ptr<Triple> TheTriple,
@@ -199,6 +205,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
Logger(Logger), InitialDynoStats(isAArch64()) {
RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
+ ICFLevelVar = parseICFLevel();
}
BinaryContext::~BinaryContext() {
@@ -2017,7 +2024,7 @@ static bool skipInstruction(const MCInst &Inst, const BinaryContext &BC) {
BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst));
}
void BinaryContext::processInstructionForFuncReferences(const MCInst &Inst) {
- if (!opts::SafeICF || skipInstruction(Inst, *this))
+ if (ICFLevelVar != ICFLevel::Safe || skipInstruction(Inst, *this))
return;
for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
if (Op.isExpr()) {
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index f09482dc08c212..6e00ccda338556 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -550,7 +550,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
LLVM_DEBUG(SinglePass.stopTimer());
};
- if (IsSafeICF) {
+ if (BC.getICFLevel() == BinaryContext::ICFLevel::Safe) {
if (Error Err = createFoldSkipList(BC)) {
return Err;
}
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index df612c0a8f2e51..e5fb1f5b257850 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -54,8 +54,6 @@ extern cl::opt<bool> PrintDynoStats;
extern cl::opt<bool> DumpDotAll;
extern cl::opt<std::string> AsmDump;
extern cl::opt<bolt::PLTCall::OptType> PLT;
-extern cl::opt<bool> ICF;
-extern cl::opt<bool> SafeICF;
static cl::opt<bool>
DynoStatsAll("dyno-stats-all",
@@ -396,9 +394,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<StripRepRet>(NeverPrint),
opts::StripRepRet);
- Manager.registerPass(
- std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
- opts::ICF || opts::SafeICF);
+ Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
+ BC.getICFLevel() != BinaryContext::ICFLevel::None);
Manager.registerPass(
std::make_unique<SpecializeMemcpy1>(NeverPrint, opts::SpecializeMemcpy1),
@@ -422,9 +419,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<Inliner>(PrintInline));
- Manager.registerPass(
- std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
- opts::ICF || opts::SafeICF);
+ Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
+ BC.getICFLevel() != BinaryContext::ICFLevel::None);
Manager.registerPass(std::make_unique<PLTCall>(PrintPLT));
diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp
index 1a8eb5cd2ce39d..5d3021ea845e7e 100644
--- a/bolt/lib/Rewrite/BoltDiff.cpp
+++ b/bolt/lib/Rewrite/BoltDiff.cpp
@@ -698,8 +698,8 @@ void RewriteInstance::compare(RewriteInstance &RI2) {
}
// Pre-pass ICF
- if (opts::ICF) {
- IdenticalCodeFolding ICF(opts::NeverPrint, opts::SafeICF);
+ if (BC->getICFLevel() != BinaryContext::ICFLevel::None) {
+ IdenticalCodeFolding ICF(opts::NeverPrint);
outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
BC->logBOLTErrorsAndQuitOnFatal(ICF.runOnFunctions(*BC));
outs() << "BOLT-DIFF: Starting ICF pass for binary 2";
diff --git a/bolt/test/X86/icf-safe-icp.test b/bolt/test/X86/icf-safe-icp.test
index efeaf469eedeea..7b46c6297a40ec 100644
--- a/bolt/test/X86/icf-safe-icp.test
+++ b/bolt/test/X86/icf-safe-icp.test
@@ -6,7 +6,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFICPTest.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test1-no-cfg.test b/bolt/test/X86/icf-safe-test1-no-cfg.test
index 37c5debe8c814b..5efbff9b3f3f76 100644
--- a/bolt/test/X86/icf-safe-test1-no-cfg.test
+++ b/bolt/test/X86/icf-safe-test1-no-cfg.test
@@ -4,8 +4,8 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
-# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf --skip-funcs=_Z7helper1PFiiiEii,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=all -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf --skip-funcs=_Z7helper1PFiiiEii,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test1-no-relocs.test b/bolt/test/X86/icf-safe-test1-no-relocs.test
index 3b5d7400d85115..c80d35a5f576ff 100644
--- a/bolt/test/X86/icf-safe-test1-no-relocs.test
+++ b/bolt/test/X86/icf-safe-test1-no-relocs.test
@@ -1,11 +1,11 @@
-## Checks that BOLT handles correctly with no relocations with --safe-icf option.
+## Checks that BOLT handles correctly with no relocations with --icf=safe option.
# REQUIRES: system-linux
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: not llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: not llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
index 2b41633d07bf93..96986dec1e1922 100644
--- a/bolt/test/X86/icf-safe-test1.test
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -5,7 +5,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO0.test b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
index 904d792e225b9c..9464dc05782ee5 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO0.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
@@ -5,7 +5,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO3.test b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
index 288faf252ad745..935a5ddb189ef7 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO3.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
@@ -5,7 +5,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO0.test b/bolt/test/X86/icf-safe-test3LocalVarO0.test
index 56a329b2f7a598..9fb783539da39f 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO0.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO0.test
@@ -5,7 +5,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO3.test b/bolt/test/X86/icf-safe-test3LocalVarO3.test
index d0782e46b17e06..55fa11ada15490 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO3.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO3.test
@@ -5,7 +5,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s -o %t2.o
# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
## Check that BOLT successfully folded a function with jump table:
# ICFCHECK: ICF iteration 1
>From d794fb02ad8312892a4d935a77ac03e4cdf98522 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Fri, 15 Nov 2024 14:29:54 -0800
Subject: [PATCH 3/6] addressed comments
---
bolt/include/bolt/Core/BinaryContext.h | 9 ++++-----
bolt/include/bolt/Core/BinaryFunction.h | 2 +-
.../include/bolt/Passes/IdenticalCodeFolding.h | 5 +++--
bolt/lib/Core/BinaryContext.cpp | 18 +++++++++---------
bolt/lib/Passes/IdenticalCodeFolding.cpp | 8 ++++----
5 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 3e58ec502acd09..318535b2fac239 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -278,9 +278,8 @@ class BinaryContext {
public:
enum class ICFLevel {
None,
- Safe, // Safe ICF for all sections.
- All, // Aggressive ICF for code, but safe ICF for data, similar to MSVC's
- // behavior.
+ Safe,
+ All,
};
static Expected<std::unique_ptr<BinaryContext>>
createBinaryContext(Triple TheTriple, StringRef InputFileName,
@@ -673,7 +672,7 @@ class BinaryContext {
std::unique_ptr<MCAsmBackend> MAB;
/// ICF level to use for this binary.
- ICFLevel ICFLevelVar{ICFLevel::None};
+ ICFLevel CurrICFLevel{ICFLevel::None};
/// Allows BOLT to print to log whenever it is necessary (with or without
/// const references)
@@ -1494,7 +1493,7 @@ class BinaryContext {
return *IOAddressMap;
}
- ICFLevel getICFLevel() const { return ICFLevelVar; }
+ ICFLevel getICFLevel() const { return CurrICFLevel; }
raw_ostream &outs() const { return Logger.Out; }
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 5e1ddcc6bff9a2..f05ea40cfb1334 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -824,7 +824,7 @@ class BinaryFunction {
bool isSafeToICF() const { return IsSafeToICF; }
/// Sets the function is not safe to fold.
- void setUnsetToICF() { IsSafeToICF = false; }
+ void setUnsafeICF() { IsSafeToICF = false; }
/// Returns the raw binary encoding of this function.
ErrorOr<ArrayRef<uint8_t>> getData() const;
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index 41fb17408d5876..eb803031bf0f0e 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -38,8 +38,9 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
Error runOnFunctions(BinaryContext &BC) override;
private:
- /// Create a skip list of functions that should not be folded.
- Error createFoldSkipList(BinaryContext &BC);
+ /// Analyses .text section and relocations and marks functions that are not
+ /// safe to fold.
+ Error markFunctionsUnsafeToFold(BinaryContext &BC);
/// Processes relocations in the .data section to identify function
/// references.
void processDataRelocations(BinaryContext &BC,
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 6cf1562735eb45..960849371660b2 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -205,7 +205,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
Logger(Logger), InitialDynoStats(isAArch64()) {
RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
- ICFLevelVar = parseICFLevel();
+ CurrICFLevel = parseICFLevel();
}
BinaryContext::~BinaryContext() {
@@ -2024,16 +2024,16 @@ static bool skipInstruction(const MCInst &Inst, const BinaryContext &BC) {
BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst));
}
void BinaryContext::processInstructionForFuncReferences(const MCInst &Inst) {
- if (ICFLevelVar != ICFLevel::Safe || skipInstruction(Inst, *this))
+ if (CurrICFLevel != ICFLevel::Safe || skipInstruction(Inst, *this))
return;
for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
- if (Op.isExpr()) {
- const MCExpr &Expr = *Op.getExpr();
- if (Expr.getKind() == MCExpr::SymbolRef) {
- const MCSymbol &Symbol = cast<MCSymbolRefExpr>(Expr).getSymbol();
- if (BinaryFunction *BF = getFunctionForSymbol(&Symbol))
- BF->setUnsetToICF();
- }
+ if (!Op.isExpr())
+ continue;
+ const MCExpr &Expr = *Op.getExpr();
+ if (Expr.getKind() == MCExpr::SymbolRef) {
+ const MCSymbol &Symbol = cast<MCSymbolRefExpr>(Expr).getSymbol();
+ if (BinaryFunction *BF = getFunctionForSymbol(&Symbol))
+ BF->setUnsafeICF();
}
}
}
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 6e00ccda338556..8fb22897a44f62 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -355,16 +355,16 @@ void IdenticalCodeFolding::processDataRelocations(
const uint64_t SymbolAddress = cantFail(Symbol.getAddress());
const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(OwningObj);
if (!ELFObj)
- assert(false && "Only ELFObjectFileBase is supported");
+ llvm_unreachable("Only ELFObjectFileBase is supported");
const int64_t Addend = BinaryContext::getRelocationAddend(ELFObj, Rel);
BinaryFunction *BF = BC.getBinaryFunctionAtAddress(SymbolAddress + Addend);
if (!BF)
continue;
- BF->setUnsetToICF();
+ BF->setUnsafeICF();
}
}
-Error IdenticalCodeFolding::createFoldSkipList(BinaryContext &BC) {
+Error IdenticalCodeFolding::markFunctionsUnsafeToFold(BinaryContext &BC) {
Error ErrorStatus = Error::success();
ErrorOr<BinarySection &> SecRelData = BC.getUniqueSectionByName(".rela.data");
if (!BC.HasRelocations)
@@ -551,7 +551,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
LLVM_DEBUG(SinglePass.stopTimer());
};
if (BC.getICFLevel() == BinaryContext::ICFLevel::Safe) {
- if (Error Err = createFoldSkipList(BC)) {
+ if (Error Err = markFunctionsUnsafeToFold(BC)) {
return Err;
}
}
>From 6e853ca31324fdc32a172fb9c48d550ceab3547c Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Fri, 15 Nov 2024 16:41:00 -0800
Subject: [PATCH 4/6] Added four tests for pic/no-pic mode. Tests for global
const function pointer
---
.../Inputs/helperSafeICFGlobalConstPtrNoPic.s | 83 ++++
...lperSafeICFGlobalConstPtrNoPicExtFuncRef.s | 84 ++++
.../Inputs/helperSafeICFGlobalConstPtrPic.s | 84 ++++
...helperSafeICFGlobalConstPtrPicExtFuncRef.s | 84 ++++
.../Inputs/mainSafeICFGlobalConstPtrNoPic.s | 339 ++++++++++++++++
...mainSafeICFGlobalConstPtrNoPicExtFuncRef.s | 343 +++++++++++++++++
.../X86/Inputs/mainSafeICFGlobalConstPtrPic.s | 350 +++++++++++++++++
.../mainSafeICFGlobalConstPtrPicExtFuncRef.s | 364 ++++++++++++++++++
.../icf-safe-test2GlobalConstPtrNoPic.test | 24 ++
...fe-test2GlobalConstPtrNoPicExtFuncRef.test | 24 ++
.../X86/icf-safe-test2GlobalConstPtrPic.test | 24 ++
...safe-test2GlobalConstPtrPicExtFuncRef.test | 24 ++
12 files changed, 1827 insertions(+)
create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s
create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s
create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s
create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s
create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s
create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s
new file mode 100644
index 00000000000000..8697f77862e66f
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s
@@ -0,0 +1,83 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+# return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+# return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+ .text
+ .file "helper.cpp"
+ .globl _Z12barAddHdlperii # -- Begin function _Z12barAddHdlperii
+ .p2align 4, 0x90
+ .type _Z12barAddHdlperii, at function
+_Z12barAddHdlperii: # @_Z12barAddHdlperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+ .cfi_endproc
+ # -- End function
+ .globl _Z19fooGlobalFuncHelperii # -- Begin function _Z19fooGlobalFuncHelperii
+ .p2align 4, 0x90
+ .type _Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii: # @_Z19fooGlobalFuncHelperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %edi
+ movl -8(%rbp), %esi
+ callq _Z12barAddHdlperii
+ addl $5, %eax
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+ .cfi_endproc
+ # -- End function
+ .type FooVar, at object # @FooVar
+ .data
+ .globl FooVar
+ .p2align 2, 0x0
+FooVar:
+ .long 1 # 0x1
+ .size FooVar, 4
+
+ .type BarVar, at object # @BarVar
+ .globl BarVar
+ .p2align 2, 0x0
+BarVar:
+ .long 2 # 0x2
+ .size BarVar, 4
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s
new file mode 100644
index 00000000000000..489400c3ccaa63
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s
@@ -0,0 +1,84 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+# return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+# return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+
+ .text
+ .file "helper.cpp"
+ .globl _Z12barAddHdlperii # -- Begin function _Z12barAddHdlperii
+ .p2align 4, 0x90
+ .type _Z12barAddHdlperii, at function
+_Z12barAddHdlperii: # @_Z12barAddHdlperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+ .cfi_endproc
+ # -- End function
+ .globl _Z19fooGlobalFuncHelperii # -- Begin function _Z19fooGlobalFuncHelperii
+ .p2align 4, 0x90
+ .type _Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii: # @_Z19fooGlobalFuncHelperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %edi
+ movl -8(%rbp), %esi
+ callq _Z12barAddHdlperii
+ addl $5, %eax
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+ .cfi_endproc
+ # -- End function
+ .type FooVar, at object # @FooVar
+ .data
+ .globl FooVar
+ .p2align 2, 0x0
+FooVar:
+ .long 1 # 0x1
+ .size FooVar, 4
+
+ .type BarVar, at object # @BarVar
+ .globl BarVar
+ .p2align 2, 0x0
+BarVar:
+ .long 2 # 0x2
+ .size BarVar, 4
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s
new file mode 100644
index 00000000000000..489400c3ccaa63
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s
@@ -0,0 +1,84 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+# return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+# return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+
+ .text
+ .file "helper.cpp"
+ .globl _Z12barAddHdlperii # -- Begin function _Z12barAddHdlperii
+ .p2align 4, 0x90
+ .type _Z12barAddHdlperii, at function
+_Z12barAddHdlperii: # @_Z12barAddHdlperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+ .cfi_endproc
+ # -- End function
+ .globl _Z19fooGlobalFuncHelperii # -- Begin function _Z19fooGlobalFuncHelperii
+ .p2align 4, 0x90
+ .type _Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii: # @_Z19fooGlobalFuncHelperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %edi
+ movl -8(%rbp), %esi
+ callq _Z12barAddHdlperii
+ addl $5, %eax
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+ .cfi_endproc
+ # -- End function
+ .type FooVar, at object # @FooVar
+ .data
+ .globl FooVar
+ .p2align 2, 0x0
+FooVar:
+ .long 1 # 0x1
+ .size FooVar, 4
+
+ .type BarVar, at object # @BarVar
+ .globl BarVar
+ .p2align 2, 0x0
+BarVar:
+ .long 2 # 0x2
+ .size BarVar, 4
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s
new file mode 100644
index 00000000000000..489400c3ccaa63
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s
@@ -0,0 +1,84 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+# return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+# return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+
+ .text
+ .file "helper.cpp"
+ .globl _Z12barAddHdlperii # -- Begin function _Z12barAddHdlperii
+ .p2align 4, 0x90
+ .type _Z12barAddHdlperii, at function
+_Z12barAddHdlperii: # @_Z12barAddHdlperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+ .cfi_endproc
+ # -- End function
+ .globl _Z19fooGlobalFuncHelperii # -- Begin function _Z19fooGlobalFuncHelperii
+ .p2align 4, 0x90
+ .type _Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii: # @_Z19fooGlobalFuncHelperii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %edi
+ movl -8(%rbp), %esi
+ callq _Z12barAddHdlperii
+ addl $5, %eax
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+ .cfi_endproc
+ # -- End function
+ .type FooVar, at object # @FooVar
+ .data
+ .globl FooVar
+ .p2align 2, 0x0
+FooVar:
+ .long 1 # 0x1
+ .size FooVar, 4
+
+ .type BarVar, at object # @BarVar
+ .globl BarVar
+ .p2align 2, 0x0
+BarVar:
+ .long 2 # 0x2
+ .size BarVar, 4
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s
new file mode 100644
index 00000000000000..c113f1b45ac1a2
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s
@@ -0,0 +1,339 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# MY_CONST static int (*MY_CONST funcGlobalBarAdd)(int, int) = barAdd;
+# MY_CONST int (*MY_CONST funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# MY_PRINTF("val: %d", temp);
+# return temp;
+# }
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFKiiiEii # -- Begin function _Z7helper1PFKiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii: # @_Z7helper1PFKiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ movabsq $_Z6barAddii, %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFKiiiES1_ii # -- Begin function _Z7helper2PFKiiiES1_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii: # @_Z7helper2PFKiiiES1_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ movl FooVar, %esi
+ movl BarVar, %edx
+ movabsq $_Z6barAddii, %rdi
+ callq _Z7helper1PFKiiiEii
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movl FooVar, %edx
+ movl BarVar, %ecx
+ movabsq $_Z6fooMulii, %rdi
+ movabsq $_Z6barMulii, %rsi
+ callq _Z7helper2PFKiiiES1_ii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movl FooVar, %edi
+ movl BarVar, %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp) # 4-byte Spill
+ movl FooVar, %edi
+ movl BarVar, %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -28(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -24(%rbp) # 4-byte Spill
+ movl FooVar, %edi
+ movl BarVar, %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -24(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -20(%rbp)
+ movl -20(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFKiiiEii
+ .addrsig_sym _Z7helper2PFKiiiES1_ii
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s
new file mode 100644
index 00000000000000..40f30cbbc30550
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s
@@ -0,0 +1,343 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# extern MY_CONST int barAddHdlper(int a, int b);
+# extern MY_CONST int fooGlobalFuncHelper(int a, int b);
+# MY_CONST static int (*const funcGlobalBarAdd)(int, int) = barAddHdlper;
+# MY_CONST int (* const funcGlobalBarMul)(int, int) = fooGlobalFuncHelper;
+# int main(int argc, char **argv) {
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# MY_PRINTF("val: %d", temp);
+# return temp;
+# }
+
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFKiiiEii # -- Begin function _Z7helper1PFKiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii: # @_Z7helper1PFKiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ movabsq $_Z6barAddii, %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFKiiiES1_ii # -- Begin function _Z7helper2PFKiiiES1_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii: # @_Z7helper2PFKiiiES1_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ movl FooVar, %esi
+ movl BarVar, %edx
+ movabsq $_Z12barAddHdlperii, %rdi
+ callq _Z7helper1PFKiiiEii
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movl FooVar, %edx
+ movl BarVar, %ecx
+ movabsq $_Z6fooMulii, %rdi
+ movabsq $_Z19fooGlobalFuncHelperii, %rsi
+ callq _Z7helper2PFKiiiES1_ii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movl FooVar, %edi
+ movl BarVar, %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp) # 4-byte Spill
+ movl FooVar, %edi
+ movl BarVar, %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -28(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -24(%rbp) # 4-byte Spill
+ movl FooVar, %edi
+ movl BarVar, %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -24(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -20(%rbp)
+ movl -20(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFKiiiEii
+ .addrsig_sym _Z7helper2PFKiiiES1_ii
+ .addrsig_sym _Z12barAddHdlperii
+ .addrsig_sym _Z19fooGlobalFuncHelperii
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s
new file mode 100644
index 00000000000000..16976abc7d081c
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s
@@ -0,0 +1,350 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# MY_CONST static int (*MY_CONST funcGlobalBarAdd)(int, int) = barAdd;
+# MY_CONST int (*MY_CONST funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# MY_PRINTF("val: %d", temp);
+# return temp;
+# }
+
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFKiiiEii # -- Begin function _Z7helper1PFKiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii: # @_Z7helper1PFKiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ leaq _Z6barAddii(%rip), %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFKiiiES1_ii # -- Begin function _Z7helper2PFKiiiES1_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii: # @_Z7helper2PFKiiiES1_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ leaq _Z6barAddii(%rip), %rdi
+ callq _Z7helper1PFKiiiEii
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ leaq _Z6barMulii(%rip), %rsi
+ callq _Z7helper2PFKiiiES1_ii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -28(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -24(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -24(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -20(%rbp)
+ movl -20(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6barMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFKiiiEii
+ .addrsig_sym _Z7helper2PFKiiiES1_ii
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s
new file mode 100644
index 00000000000000..a7da985b93bc38
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s
@@ -0,0 +1,364 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+# return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+# return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+# return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+# if (func == barAdd)
+# return 1;
+# return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+# if (func == func2)
+# return 2;
+# return func(a, b) + func2(a, b);
+# }
+# extern MY_CONST int barAddHdlper(int a, int b);
+# extern MY_CONST int fooGlobalFuncHelper(int a, int b);
+# MY_CONST static int (*const funcGlobalBarAdd)(int, int) = barAddHdlper;
+# MY_CONST int (* const funcGlobalBarMul)(int, int) = fooGlobalFuncHelper;
+# int main(int argc, char **argv) {
+# int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+# helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+# barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+# MY_PRINTF("val: %d", temp);
+# return temp;
+# }
+
+ .text
+ .file "main.cpp"
+ .globl _Z6fooSubii # -- Begin function _Z6fooSubii
+ .p2align 4, 0x90
+ .type _Z6fooSubii, at function
+_Z6fooSubii: # @_Z6fooSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barSubii # -- Begin function _Z6barSubii
+ .p2align 4, 0x90
+ .type _Z6barSubii, at function
+_Z6barSubii: # @_Z6barSubii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ subl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z6barSubii, .Lfunc_end1-_Z6barSubii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooMulii # -- Begin function _Z6fooMulii
+ .p2align 4, 0x90
+ .type _Z6fooMulii, at function
+_Z6fooMulii: # @_Z6fooMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barMulii # -- Begin function _Z6barMulii
+ .p2align 4, 0x90
+ .type _Z6barMulii, at function
+_Z6barMulii: # @_Z6barMulii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ imull -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6barMulii, .Lfunc_end3-_Z6barMulii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6fooAddii # -- Begin function _Z6fooAddii
+ .p2align 4, 0x90
+ .type _Z6fooAddii, at function
+_Z6fooAddii: # @_Z6fooAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end4:
+ .size _Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z6barAddii # -- Begin function _Z6barAddii
+ .p2align 4, 0x90
+ .type _Z6barAddii, at function
+_Z6barAddii: # @_Z6barAddii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+ movl %esi, -8(%rbp)
+ movl -4(%rbp), %eax
+ addl -8(%rbp), %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end5:
+ .size _Z6barAddii, .Lfunc_end5-_Z6barAddii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper1PFKiiiEii # -- Begin function _Z7helper1PFKiiiEii
+ .p2align 4, 0x90
+ .type _Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii: # @_Z7helper1PFKiiiEii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movq %rdi, -16(%rbp)
+ movl %esi, -20(%rbp)
+ movl %edx, -24(%rbp)
+ leaq _Z6barAddii(%rip), %rax
+ cmpq %rax, -16(%rbp)
+ jne .LBB6_2
+# %bb.1:
+ movl $1, -4(%rbp)
+ jmp .LBB6_3
+.LBB6_2:
+ movq -16(%rbp), %rax
+ movl -20(%rbp), %edi
+ movl -24(%rbp), %esi
+ callq *%rax
+ subl $4, %eax
+ movl %eax, -4(%rbp)
+.LBB6_3:
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end6:
+ .size _Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+ .cfi_endproc
+ # -- End function
+ .globl _Z7helper2PFKiiiES1_ii # -- Begin function _Z7helper2PFKiiiES1_ii
+ .p2align 4, 0x90
+ .type _Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii: # @_Z7helper2PFKiiiES1_ii
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movl %edx, -28(%rbp)
+ movl %ecx, -32(%rbp)
+ movq -16(%rbp), %rax
+ cmpq -24(%rbp), %rax
+ jne .LBB7_2
+# %bb.1:
+ movl $2, -4(%rbp)
+ jmp .LBB7_3
+.LBB7_2:
+ movq -16(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq -24(%rbp), %rax
+ movl -28(%rbp), %edi
+ movl -32(%rbp), %esi
+ callq *%rax
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -4(%rbp)
+.LBB7_3:
+ movl -4(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end7:
+ .size _Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main, at function
+main: # @main
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ movq _Z12barAddHdlperii at GOTPCREL(%rip), %rdi
+ callq _Z7helper1PFKiiiEii
+ movl %eax, -36(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edx
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %ecx
+ leaq _Z6fooMulii(%rip), %rdi
+ movq _Z19fooGlobalFuncHelperii at GOTPCREL(%rip), %rsi
+ callq _Z7helper2PFKiiiES1_ii
+ movl %eax, %ecx
+ movl -36(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -32(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooSubii
+ movl %eax, %ecx
+ movl -32(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -28(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6barSubii
+ movl %eax, %ecx
+ movl -28(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -24(%rbp) # 4-byte Spill
+ movq FooVar at GOTPCREL(%rip), %rax
+ movl (%rax), %edi
+ movq BarVar at GOTPCREL(%rip), %rax
+ movl (%rax), %esi
+ callq _Z6fooAddii
+ movl %eax, %ecx
+ movl -24(%rbp), %eax # 4-byte Reload
+ addl %ecx, %eax
+ movl %eax, -20(%rbp)
+ movl -20(%rbp), %esi
+ leaq .L.str(%rip), %rdi
+ movb $0, %al
+ callq printf at PLT
+ movl -20(%rbp), %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end8:
+ .size main, .Lfunc_end8-main
+ .cfi_endproc
+ # -- End function
+ .type .L.str, at object # @.str
+ .section .rodata.str1.1,"aMS", at progbits,1
+.L.str:
+ .asciz "val: %d\n"
+ .size .L.str, 9
+
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6fooSubii
+ .addrsig_sym _Z6barSubii
+ .addrsig_sym _Z6fooMulii
+ .addrsig_sym _Z6fooAddii
+ .addrsig_sym _Z6barAddii
+ .addrsig_sym _Z7helper1PFKiiiEii
+ .addrsig_sym _Z7helper2PFKiiiES1_ii
+ .addrsig_sym _Z12barAddHdlperii
+ .addrsig_sym _Z19fooGlobalFuncHelperii
+ .addrsig_sym printf
+ .addrsig_sym FooVar
+ .addrsig_sym BarVar
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
new file mode 100644
index 00000000000000..332c4296f45e5f
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer in -fno-pic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrNoPic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrNoPic.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q -no-pie
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
new file mode 100644
index 00000000000000..a46fdb86574c0b
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer in -fno-pic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q -no-pie
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
new file mode 100644
index 00000000000000..ab84fdc619ae9b
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer in -fpic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrPic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrPic.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
new file mode 100644
index 00000000000000..f416f8f5f4f9c8
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer with external function reference in -fpic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
>From e36ec02bfeb440e56a9b8593f454a9e7f2fe7ed5 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Fri, 15 Nov 2024 18:17:01 -0800
Subject: [PATCH 5/6] switched to enum version of icf compile option
---
bolt/lib/Core/BinaryContext.cpp | 24 +++++++++++-------------
bolt/test/X86/shared_object_entry.s | 2 +-
2 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 960849371660b2..aaa7a125e94147 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -76,8 +76,16 @@ cl::opt<std::string> CompDirOverride(
"to *.dwo files."),
cl::Hidden, cl::init(""), cl::cat(BoltCategory));
-cl::opt<std::string> ICF("icf", cl::desc("fold functions with identical code"),
- cl::ValueOptional, cl::cat(BoltOptCategory));
+cl::opt<bolt::BinaryContext::ICFLevel> ICF(
+ "icf", cl::desc("fold functions with identical code"),
+ cl::init(bolt::BinaryContext::ICFLevel::None),
+ cl::values(
+ clEnumValN(bolt::BinaryContext::ICFLevel::All, "all", "enable ICF"),
+ clEnumValN(bolt::BinaryContext::ICFLevel::All, "", "enable ICF"),
+ clEnumValN(bolt::BinaryContext::ICFLevel::None, "none", "disable ICF"),
+ clEnumValN(bolt::BinaryContext::ICFLevel::Safe, "safe",
+ "enable safe ICF")),
+ cl::ZeroOrMore, cl::ValueOptional, cl::cat(BoltOptCategory));
} // namespace opts
namespace {
@@ -171,16 +179,6 @@ void BinaryContext::logBOLTErrorsAndQuitOnFatal(Error E) {
});
}
-static BinaryContext::ICFLevel parseICFLevel() {
- if (!opts::ICF.getNumOccurrences())
- return BinaryContext::ICFLevel::None;
- std::string Str = StringRef(opts::ICF).lower();
- return StringSwitch<BinaryContext::ICFLevel>(Str)
- .Case("all", BinaryContext::ICFLevel::All)
- .Case("safe", BinaryContext::ICFLevel::Safe)
- .Default(BinaryContext::ICFLevel::All);
-}
-
BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
std::unique_ptr<DWARFContext> DwCtx,
std::unique_ptr<Triple> TheTriple,
@@ -205,7 +203,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
Logger(Logger), InitialDynoStats(isAArch64()) {
RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
- CurrICFLevel = parseICFLevel();
+ CurrICFLevel = opts::ICF;
}
BinaryContext::~BinaryContext() {
diff --git a/bolt/test/X86/shared_object_entry.s b/bolt/test/X86/shared_object_entry.s
index 87a3c0655533d1..2caab5afdb7805 100644
--- a/bolt/test/X86/shared_object_entry.s
+++ b/bolt/test/X86/shared_object_entry.s
@@ -2,7 +2,7 @@
# RUN: ld.lld %t.o -o %t.so --shared --entry=func1.cold.1 --emit-relocs
# RUN: llvm-bolt -relocs %t.so -o %t -reorder-functions=hfsort+ \
# RUN: -split-functions -reorder-blocks=ext-tsp -split-all-cold \
-# RUN: -dyno-stats -icf=1 -use-gnu-stack
+# RUN: -dyno-stats -icf -use-gnu-stack
## Check that an entry point is a cold symbol
# RUN: llvm-readelf -h %t.so > %t.log
>From b83c3350ec08623350c1639347cb9f8e71850fba Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Tue, 19 Nov 2024 16:21:32 -0800
Subject: [PATCH 6/6] simplified debug print, removed shared func
---
.../bolt/Passes/IdenticalCodeFolding.h | 2 ++
bolt/lib/Passes/IdenticalCodeFolding.cpp | 29 +++++--------------
bolt/test/X86/icf-safe-test1-no-cfg.test | 4 +--
bolt/test/X86/icf-safe-test1.test | 4 +--
.../icf-safe-test2GlobalConstPtrNoPic.test | 4 +--
...fe-test2GlobalConstPtrNoPicExtFuncRef.test | 6 ++--
.../X86/icf-safe-test2GlobalConstPtrPic.test | 4 +--
...safe-test2GlobalConstPtrPicExtFuncRef.test | 6 ++--
bolt/test/X86/icf-safe-test2GlobalVarO0.test | 4 +--
bolt/test/X86/icf-safe-test2GlobalVarO3.test | 4 +--
bolt/test/X86/icf-safe-test3LocalVarO0.test | 4 +--
bolt/test/X86/icf-safe-test3LocalVarO3.test | 4 +--
12 files changed, 32 insertions(+), 43 deletions(-)
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index eb803031bf0f0e..9d8d3ece1a2edf 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -27,6 +27,8 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
return false;
if (BF.hasSDTMarker())
return false;
+ if (!BF.isSafeToICF())
+ return false;
return BinaryFunctionPass::shouldOptimize(BF);
}
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 8fb22897a44f62..2114bfe874bd18 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -393,22 +393,12 @@ Error IdenticalCodeFolding::markFunctionsUnsafeToFold(BinaryContext &BC) {
"markUnsafe", /*ForceSequential*/ false, 2);
LLVM_DEBUG({
- std::vector<StringRef> Vect;
- std::mutex PrintMutex;
- ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
- if (BF.isSafeToICF())
- return;
- std::lock_guard<std::mutex> Lock(PrintMutex);
- Vect.push_back(BF.getOneName());
- };
- ParallelUtilities::PredicateTy SkipFunc =
- [&](const BinaryFunction &BF) -> bool { return false; };
- ParallelUtilities::runOnEachFunction(
- BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
- "markUnsafe", /*ForceSequential*/ false, 2);
- llvm::sort(Vect);
- for (const auto &FuncName : Vect)
- dbgs() << "BOLT-DEBUG: skipping function " << FuncName << '\n';
+ for (auto &BFIter : BC.getBinaryFunctions()) {
+ if (BFIter.second.isSafeToICF())
+ continue;
+ dbgs() << "BOLT-DEBUG: skipping function " << BFIter.second.getOneName()
+ << '\n';
+ }
});
return ErrorStatus;
}
@@ -422,9 +412,6 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
std::atomic<uint64_t> NumFoldedLastIteration{0};
CongruentBucketsMap CongruentBuckets;
- auto SkipFuncShared = [&](const BinaryFunction &BF) {
- return !shouldOptimize(BF) || !BF.isSafeToICF();
- };
// Hash all the functions
auto hashFunctions = [&]() {
NamedRegionTimer HashFunctionsTimer("hashing", "hashing", "ICF breakdown",
@@ -444,7 +431,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
};
ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
- return SkipFuncShared(BF);
+ return !shouldOptimize(BF);
};
ParallelUtilities::runOnEachFunction(
@@ -460,7 +447,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
"ICF breakdown", opts::TimeICF);
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &BF = BFI.second;
- if (SkipFuncShared(BF))
+ if (!shouldOptimize(BF))
continue;
CongruentBuckets[&BF].emplace(&BF);
}
diff --git a/bolt/test/X86/icf-safe-test1-no-cfg.test b/bolt/test/X86/icf-safe-test1-no-cfg.test
index 5efbff9b3f3f76..8617dfa590d941 100644
--- a/bolt/test/X86/icf-safe-test1-no-cfg.test
+++ b/bolt/test/X86/icf-safe-test1-no-cfg.test
@@ -13,9 +13,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
index 96986dec1e1922..b60795d7fa3bee 100644
--- a/bolt/test/X86/icf-safe-test1.test
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -13,9 +13,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
index 332c4296f45e5f..83e7e798afcd8c 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
@@ -15,9 +15,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
index a46fdb86574c0b..bef5b71a25c4f6 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
@@ -15,10 +15,10 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z12barAddHdlperii
-# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
index ab84fdc619ae9b..d2c039769e8d23 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
@@ -15,9 +15,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
index f416f8f5f4f9c8..2a93af6d44220c 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
@@ -15,10 +15,10 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z12barAddHdlperii
-# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO0.test b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
index 9464dc05782ee5..de5c1ebca81e17 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO0.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
@@ -13,9 +13,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO3.test b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
index 935a5ddb189ef7..06853bfe328904 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO3.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
@@ -13,9 +13,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO0.test b/bolt/test/X86/icf-safe-test3LocalVarO0.test
index 9fb783539da39f..0a88cca49e1bd4 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO0.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO0.test
@@ -13,9 +13,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO3.test b/bolt/test/X86/icf-safe-test3LocalVarO3.test
index 55fa11ada15490..d961980df15be6 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO3.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO3.test
@@ -13,9 +13,9 @@
# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
-# SAFEICFCHECK: skipping function _Z6barAddii
+# SAFEICFCHECK: skipping function _Z6fooMulii
# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
# SAFEICFCHECK-NEXT: ICF iteration 1
# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
# SAFEICFCHECK-NEXT: ===---------
More information about the llvm-commits
mailing list