[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