[llvm] [BOLT] Add support for safe-icf (PR #116275)
Alexander Yermolovich via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 14 12:14:33 PST 2024
https://github.com/ayermolo created https://github.com/llvm/llvm-project/pull/116275
Identical Code Folding (ICF) folds functions that are "identical" into one function, and updates symbol addresses to the new address. This reduces the size of a binary, but can lead to problems. For example when function pointers are compared. Either explicitly in the code or generated code by optimization passes like Indirect Call Promotion (ICP). After ICF what used to be two different addresses become the same address. This can lead to a different code path being taken.
This is where Safe ICF comes in. Linker (LLD) does it using address significant section generated by clang. If symbol is in it, or an object doesn't have this section symbols are not folded.
BOLT does not have the information regarding which objects do not have this section, so can't re-use this mechanism.
This implementation scans .text section and conservatively marks functions which symbols, and are not used in the control flow instructions, as unsafe. It also scans through the .rela.data section and does the same for relocations that reference functions. The latter handles the case when function pointer is stored in a local or global variable.
>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] 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: ===---------
More information about the llvm-commits
mailing list