[llvm] [BOLT] Add support for safe-icf (PR #116275)

Alexander Yermolovich via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 19 16:22:06 PST 2024


https://github.com/ayermolo updated https://github.com/llvm/llvm-project/pull/116275

>From 03ca42cce937fc7a85104e80fc271095e998b472 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Mon, 11 Nov 2024 19:19:02 -0800
Subject: [PATCH 1/6] safe-icf

Summary:

Test Plan:

Reviewers:

Subscribers:

Tasks:

Tags:

Differential Revision: https://phabricator.intern.facebook.com/D65799965
---
 bolt/include/bolt/Core/BinaryContext.h        |   9 +
 bolt/include/bolt/Core/BinaryFunction.h       |   9 +
 .../bolt/Passes/IdenticalCodeFolding.h        |  14 +-
 bolt/lib/Core/BinaryContext.cpp               |  86 +++++
 bolt/lib/Core/BinaryFunction.cpp              |   2 +
 bolt/lib/Passes/IdenticalCodeFolding.cpp      |  85 +++-
 bolt/lib/Rewrite/BinaryPassManager.cpp        |  15 +-
 bolt/lib/Rewrite/BoltDiff.cpp                 |   3 +-
 bolt/lib/Rewrite/RewriteInstance.cpp          |  64 +--
 bolt/test/X86/Inputs/helperSafeICF.s          |  39 ++
 bolt/test/X86/Inputs/helperSafeICFICPTest.s   |  40 ++
 bolt/test/X86/Inputs/mainSafeICFICPTest.s     | 331 ++++++++++++++++
 bolt/test/X86/Inputs/mainSafeICFTest1.s       | 345 +++++++++++++++++
 .../X86/Inputs/mainSafeICFTest2GlobalVarO0.s  | 363 ++++++++++++++++++
 .../X86/Inputs/mainSafeICFTest2GlobalVarO3.s  | 293 ++++++++++++++
 .../X86/Inputs/mainSafeICFTest3LocalVarO0.s   | 359 +++++++++++++++++
 .../X86/Inputs/mainSafeICFTest3LocalVarO3.s   | 286 ++++++++++++++
 bolt/test/X86/icf-safe-icp.test               |  20 +
 bolt/test/X86/icf-safe-test1-no-cfg.test      |  21 +
 bolt/test/X86/icf-safe-test1-no-relocs.test   |  16 +
 bolt/test/X86/icf-safe-test1.test             |  21 +
 bolt/test/X86/icf-safe-test2GlobalVarO0.test  |  21 +
 bolt/test/X86/icf-safe-test2GlobalVarO3.test  |  21 +
 bolt/test/X86/icf-safe-test3LocalVarO0.test   |  21 +
 bolt/test/X86/icf-safe-test3LocalVarO3.test   |  21 +
 25 files changed, 2431 insertions(+), 74 deletions(-)
 create mode 100644 bolt/test/X86/Inputs/helperSafeICF.s
 create mode 100644 bolt/test/X86/Inputs/helperSafeICFICPTest.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFICPTest.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest1.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s
 create mode 100644 bolt/test/X86/icf-safe-icp.test
 create mode 100644 bolt/test/X86/icf-safe-test1-no-cfg.test
 create mode 100644 bolt/test/X86/icf-safe-test1-no-relocs.test
 create mode 100644 bolt/test/X86/icf-safe-test1.test
 create mode 100644 bolt/test/X86/icf-safe-test2GlobalVarO0.test
 create mode 100644 bolt/test/X86/icf-safe-test2GlobalVarO3.test
 create mode 100644 bolt/test/X86/icf-safe-test3LocalVarO0.test
 create mode 100644 bolt/test/X86/icf-safe-test3LocalVarO3.test

diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 08ce892054874c..92889ffe162ae1 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -282,6 +282,12 @@ class BinaryContext {
                       std::unique_ptr<DWARFContext> DwCtx,
                       JournalingStreams Logger);
 
+  /// Returns addend of a relocation.
+  static int64_t getRelocationAddend(const ELFObjectFileBase *Obj,
+                                     const RelocationRef &Rel);
+  /// Returns symbol of a relocation.
+  static uint32_t getRelocationSymbol(const ELFObjectFileBase *Obj,
+                                      const RelocationRef &Rel);
   /// Superset of compiler units that will contain overwritten code that needs
   /// new debug info. In a few cases, functions may end up not being
   /// overwritten, but it is okay to re-generate debug info for them.
@@ -1350,6 +1356,9 @@ class BinaryContext {
     return Code.size();
   }
 
+  /// Processes .text section to identify function references.
+  void processInstructionForFuncReferences(const MCInst &Inst);
+
   /// Compute the native code size for a range of instructions.
   /// Note: this can be imprecise wrt the final binary since happening prior to
   /// relaxation, as well as wrt the original binary because of opcode
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 0b3682353f736e..5e1ddcc6bff9a2 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -428,6 +428,9 @@ class BinaryFunction {
   /// Function order for streaming into the destination binary.
   uint32_t Index{-1U};
 
+  /// Indicates if the function is safe to fold.
+  bool IsSafeToICF{true};
+
   /// Get basic block index assuming it belongs to this function.
   unsigned getIndex(const BinaryBasicBlock *BB) const {
     assert(BB->getIndex() < BasicBlocks.size());
@@ -817,6 +820,12 @@ class BinaryFunction {
     return nullptr;
   }
 
+  /// Indicates if the function is safe to fold.
+  bool isSafeToICF() const { return IsSafeToICF; }
+
+  /// Sets the function is not safe to fold.
+  void setUnsetToICF() { IsSafeToICF = false; }
+
   /// Returns the raw binary encoding of this function.
   ErrorOr<ArrayRef<uint8_t>> getData() const;
 
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index b4206fa3607445..75d5f19ddfc7be 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -31,11 +31,21 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
   }
 
 public:
-  explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass)
-      : BinaryFunctionPass(PrintPass) {}
+  explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass, bool IsSafeICF)
+      : BinaryFunctionPass(PrintPass), IsSafeICF(IsSafeICF) {}
 
   const char *getName() const override { return "identical-code-folding"; }
   Error runOnFunctions(BinaryContext &BC) override;
+
+private:
+  /// Create a skip list of functions that should not be folded.
+  Error createFoldSkipList(BinaryContext &BC);
+  /// Processes relocations in the .data section to identify function
+  /// references.
+  void processDataRelocations(BinaryContext &BC,
+                              const SectionRef &SecRefRelData);
+
+  bool IsSafeICF;
 };
 
 } // namespace bolt
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index f246750209d6c4..31352bed6fdab7 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -75,8 +75,63 @@ cl::opt<std::string> CompDirOverride(
              "location, which is used with DW_AT_dwo_name to construct a path "
              "to *.dwo files."),
     cl::Hidden, cl::init(""), cl::cat(BoltCategory));
+
+cl::opt<bool> ICF("icf", cl::desc("fold functions with identical code"),
+                  cl::cat(BoltOptCategory));
+
+cl::opt<bool> SafeICF("safe-icf",
+                      cl::desc("Enable safe identical code folding"),
+                      cl::cat(BoltOptCategory));
 } // namespace opts
 
+namespace {
+template <typename ELFT>
+int64_t getRelocationAddend(const ELFObjectFile<ELFT> *Obj,
+                            const RelocationRef &RelRef) {
+  using ELFShdrTy = typename ELFT::Shdr;
+  using Elf_Rela = typename ELFT::Rela;
+  int64_t Addend = 0;
+  const ELFFile<ELFT> &EF = Obj->getELFFile();
+  DataRefImpl Rel = RelRef.getRawDataRefImpl();
+  const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
+  switch (RelocationSection->sh_type) {
+  default:
+    llvm_unreachable("unexpected relocation section type");
+  case ELF::SHT_REL:
+    break;
+  case ELF::SHT_RELA: {
+    const Elf_Rela *RelA = Obj->getRela(Rel);
+    Addend = RelA->r_addend;
+    break;
+  }
+  }
+
+  return Addend;
+}
+
+template <typename ELFT>
+uint32_t getRelocationSymbol(const ELFObjectFile<ELFT> *Obj,
+                             const RelocationRef &RelRef) {
+  using ELFShdrTy = typename ELFT::Shdr;
+  uint32_t Symbol = 0;
+  const ELFFile<ELFT> &EF = Obj->getELFFile();
+  DataRefImpl Rel = RelRef.getRawDataRefImpl();
+  const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
+  switch (RelocationSection->sh_type) {
+  default:
+    llvm_unreachable("unexpected relocation section type");
+  case ELF::SHT_REL:
+    Symbol = Obj->getRel(Rel)->getSymbol(EF.isMips64EL());
+    break;
+  case ELF::SHT_RELA:
+    Symbol = Obj->getRela(Rel)->getSymbol(EF.isMips64EL());
+    break;
+  }
+
+  return Symbol;
+}
+} // anonymous namespace
+
 namespace llvm {
 namespace bolt {
 
@@ -156,6 +211,16 @@ BinaryContext::~BinaryContext() {
   clearBinaryData();
 }
 
+uint32_t BinaryContext::getRelocationSymbol(const ELFObjectFileBase *Obj,
+                                            const RelocationRef &Rel) {
+  return ::getRelocationSymbol(cast<ELF64LEObjectFile>(Obj), Rel);
+}
+
+int64_t BinaryContext::getRelocationAddend(const ELFObjectFileBase *Obj,
+                                           const RelocationRef &Rel) {
+  return ::getRelocationAddend(cast<ELF64LEObjectFile>(Obj), Rel);
+}
+
 /// Create BinaryContext for a given architecture \p ArchName and
 /// triple \p TripleName.
 Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
@@ -1945,6 +2010,27 @@ static void printDebugInfo(raw_ostream &OS, const MCInst &Instruction,
     OS << " discriminator:" << Row.Discriminator;
 }
 
+static bool skipInstruction(const MCInst &Inst, const BinaryContext &BC) {
+  const bool IsX86 = BC.isX86();
+  return (BC.MIB->isPseudo(Inst) || BC.MIB->isUnconditionalBranch(Inst) ||
+          (IsX86 && BC.MIB->isConditionalBranch(Inst)) ||
+          BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst));
+}
+void BinaryContext::processInstructionForFuncReferences(const MCInst &Inst) {
+  if (!opts::SafeICF || skipInstruction(Inst, *this))
+    return;
+  for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
+    if (Op.isExpr()) {
+      const MCExpr &Expr = *Op.getExpr();
+      if (Expr.getKind() == MCExpr::SymbolRef) {
+        const MCSymbol &Symbol = cast<MCSymbolRefExpr>(Expr).getSymbol();
+        if (BinaryFunction *BF = getFunctionForSymbol(&Symbol))
+          BF->setUnsetToICF();
+      }
+    }
+  }
+}
+
 void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction,
                                      uint64_t Offset,
                                      const BinaryFunction *Function,
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index c12217d549479b..07d386efb26ec5 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1626,6 +1626,8 @@ bool BinaryFunction::scanExternalRefs() {
     if (!BC.HasRelocations)
       continue;
 
+    BC.processInstructionForFuncReferences(Instruction);
+
     if (BranchTargetSymbol) {
       BC.MIB->replaceBranchTarget(Instruction, BranchTargetSymbol,
                                   Emitter.LocalCtx.get());
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 38e080c9dd6213..f09482dc08c212 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -11,8 +11,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "bolt/Passes/IdenticalCodeFolding.h"
+#include "bolt/Core/BinaryContext.h"
 #include "bolt/Core/HashUtilities.h"
 #include "bolt/Core/ParallelUtilities.h"
+#include "bolt/Rewrite/RewriteInstance.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ThreadPool.h"
@@ -31,6 +33,7 @@ using namespace bolt;
 namespace opts {
 
 extern cl::OptionCategory BoltOptCategory;
+extern cl::opt<unsigned> Verbosity;
 
 static cl::opt<bool>
     ICFUseDFS("icf-dfs", cl::desc("use DFS ordering when using -icf option"),
@@ -341,6 +344,75 @@ typedef std::unordered_map<BinaryFunction *, std::vector<BinaryFunction *>,
 namespace llvm {
 namespace bolt {
 
+void IdenticalCodeFolding::processDataRelocations(
+    BinaryContext &BC, const SectionRef &SecRefRelData) {
+  for (const RelocationRef &Rel : SecRefRelData.relocations()) {
+    symbol_iterator SymbolIter = Rel.getSymbol();
+    const ObjectFile *OwningObj = Rel.getObject();
+    assert(SymbolIter != OwningObj->symbol_end() &&
+           "relocation Symbol expected");
+    const SymbolRef &Symbol = *SymbolIter;
+    const uint64_t SymbolAddress = cantFail(Symbol.getAddress());
+    const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(OwningObj);
+    if (!ELFObj)
+      assert(false && "Only ELFObjectFileBase is supported");
+    const int64_t Addend = BinaryContext::getRelocationAddend(ELFObj, Rel);
+    BinaryFunction *BF = BC.getBinaryFunctionAtAddress(SymbolAddress + Addend);
+    if (!BF)
+      continue;
+    BF->setUnsetToICF();
+  }
+}
+
+Error IdenticalCodeFolding::createFoldSkipList(BinaryContext &BC) {
+  Error ErrorStatus = Error::success();
+  ErrorOr<BinarySection &> SecRelData = BC.getUniqueSectionByName(".rela.data");
+  if (!BC.HasRelocations)
+    ErrorStatus = joinErrors(
+        std::move(ErrorStatus),
+        createFatalBOLTError(Twine("BOLT-ERROR: Binary built without "
+                                   "relocations. Safe ICF is not supported")));
+  if (ErrorStatus)
+    return ErrorStatus;
+  if (SecRelData) {
+    SectionRef SecRefRelData = SecRelData->getSectionRef();
+    processDataRelocations(BC, SecRefRelData);
+  }
+
+  ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+    if (BF.getState() == BinaryFunction::State::CFG) {
+      for (const BinaryBasicBlock *BB : BF.getLayout().blocks())
+        for (const MCInst &Inst : *BB)
+          BC.processInstructionForFuncReferences(Inst);
+    }
+  };
+  ParallelUtilities::PredicateTy SkipFunc =
+      [&](const BinaryFunction &BF) -> bool { return (bool)ErrorStatus; };
+  ParallelUtilities::runOnEachFunction(
+      BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
+      "markUnsafe", /*ForceSequential*/ false, 2);
+
+  LLVM_DEBUG({
+    std::vector<StringRef> Vect;
+    std::mutex PrintMutex;
+    ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+      if (BF.isSafeToICF())
+        return;
+      std::lock_guard<std::mutex> Lock(PrintMutex);
+      Vect.push_back(BF.getOneName());
+    };
+    ParallelUtilities::PredicateTy SkipFunc =
+        [&](const BinaryFunction &BF) -> bool { return false; };
+    ParallelUtilities::runOnEachFunction(
+        BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
+        "markUnsafe", /*ForceSequential*/ false, 2);
+    llvm::sort(Vect);
+    for (const auto &FuncName : Vect)
+      dbgs() << "BOLT-DEBUG: skipping function " << FuncName << '\n';
+  });
+  return ErrorStatus;
+}
+
 Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
   const size_t OriginalFunctionCount = BC.getBinaryFunctions().size();
   uint64_t NumFunctionsFolded = 0;
@@ -350,6 +422,9 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
   std::atomic<uint64_t> NumFoldedLastIteration{0};
   CongruentBucketsMap CongruentBuckets;
 
+  auto SkipFuncShared = [&](const BinaryFunction &BF) {
+    return !shouldOptimize(BF) || !BF.isSafeToICF();
+  };
   // Hash all the functions
   auto hashFunctions = [&]() {
     NamedRegionTimer HashFunctionsTimer("hashing", "hashing", "ICF breakdown",
@@ -369,7 +444,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
     };
 
     ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
-      return !shouldOptimize(BF);
+      return SkipFuncShared(BF);
     };
 
     ParallelUtilities::runOnEachFunction(
@@ -385,7 +460,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
                                            "ICF breakdown", opts::TimeICF);
     for (auto &BFI : BC.getBinaryFunctions()) {
       BinaryFunction &BF = BFI.second;
-      if (!this->shouldOptimize(BF))
+      if (SkipFuncShared(BF))
         continue;
       CongruentBuckets[&BF].emplace(&BF);
     }
@@ -475,7 +550,11 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
 
     LLVM_DEBUG(SinglePass.stopTimer());
   };
-
+  if (IsSafeICF) {
+    if (Error Err = createFoldSkipList(BC)) {
+      return Err;
+    }
+  }
   hashFunctions();
   createCongruentBuckets();
 
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index b0906041833484..df612c0a8f2e51 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -54,6 +54,8 @@ extern cl::opt<bool> PrintDynoStats;
 extern cl::opt<bool> DumpDotAll;
 extern cl::opt<std::string> AsmDump;
 extern cl::opt<bolt::PLTCall::OptType> PLT;
+extern cl::opt<bool> ICF;
+extern cl::opt<bool> SafeICF;
 
 static cl::opt<bool>
 DynoStatsAll("dyno-stats-all",
@@ -65,9 +67,6 @@ static cl::opt<bool>
                          cl::desc("eliminate unreachable code"), cl::init(true),
                          cl::cat(BoltOptCategory));
 
-cl::opt<bool> ICF("icf", cl::desc("fold functions with identical code"),
-                  cl::cat(BoltOptCategory));
-
 static cl::opt<bool> JTFootprintReductionFlag(
     "jt-footprint-reduction",
     cl::desc("make jump tables size smaller at the cost of using more "
@@ -397,8 +396,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
     Manager.registerPass(std::make_unique<StripRepRet>(NeverPrint),
                          opts::StripRepRet);
 
-  Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
-                       opts::ICF);
+  Manager.registerPass(
+      std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
+      opts::ICF || opts::SafeICF);
 
   Manager.registerPass(
       std::make_unique<SpecializeMemcpy1>(NeverPrint, opts::SpecializeMemcpy1),
@@ -422,8 +422,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
 
   Manager.registerPass(std::make_unique<Inliner>(PrintInline));
 
-  Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
-                       opts::ICF);
+  Manager.registerPass(
+      std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
+      opts::ICF || opts::SafeICF);
 
   Manager.registerPass(std::make_unique<PLTCall>(PrintPLT));
 
diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp
index 74b5ca18abce42..1a8eb5cd2ce39d 100644
--- a/bolt/lib/Rewrite/BoltDiff.cpp
+++ b/bolt/lib/Rewrite/BoltDiff.cpp
@@ -29,6 +29,7 @@ namespace opts {
 extern cl::OptionCategory BoltDiffCategory;
 extern cl::opt<bool> NeverPrint;
 extern cl::opt<bool> ICF;
+extern cl::opt<bool> SafeICF;
 
 static cl::opt<bool> IgnoreLTOSuffix(
     "ignore-lto-suffix",
@@ -698,7 +699,7 @@ void RewriteInstance::compare(RewriteInstance &RI2) {
 
   // Pre-pass ICF
   if (opts::ICF) {
-    IdenticalCodeFolding ICF(opts::NeverPrint);
+    IdenticalCodeFolding ICF(opts::NeverPrint, opts::SafeICF);
     outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
     BC->logBOLTErrorsAndQuitOnFatal(ICF.runOnFunctions(*BC));
     outs() << "BOLT-DIFF: Starting ICF pass for binary 2";
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index a4c50cbc3e2bbf..70c9eeef3cdc57 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2111,64 +2111,6 @@ void RewriteInstance::adjustCommandLineOptions() {
   }
 }
 
-namespace {
-template <typename ELFT>
-int64_t getRelocationAddend(const ELFObjectFile<ELFT> *Obj,
-                            const RelocationRef &RelRef) {
-  using ELFShdrTy = typename ELFT::Shdr;
-  using Elf_Rela = typename ELFT::Rela;
-  int64_t Addend = 0;
-  const ELFFile<ELFT> &EF = Obj->getELFFile();
-  DataRefImpl Rel = RelRef.getRawDataRefImpl();
-  const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
-  switch (RelocationSection->sh_type) {
-  default:
-    llvm_unreachable("unexpected relocation section type");
-  case ELF::SHT_REL:
-    break;
-  case ELF::SHT_RELA: {
-    const Elf_Rela *RelA = Obj->getRela(Rel);
-    Addend = RelA->r_addend;
-    break;
-  }
-  }
-
-  return Addend;
-}
-
-int64_t getRelocationAddend(const ELFObjectFileBase *Obj,
-                            const RelocationRef &Rel) {
-  return getRelocationAddend(cast<ELF64LEObjectFile>(Obj), Rel);
-}
-
-template <typename ELFT>
-uint32_t getRelocationSymbol(const ELFObjectFile<ELFT> *Obj,
-                             const RelocationRef &RelRef) {
-  using ELFShdrTy = typename ELFT::Shdr;
-  uint32_t Symbol = 0;
-  const ELFFile<ELFT> &EF = Obj->getELFFile();
-  DataRefImpl Rel = RelRef.getRawDataRefImpl();
-  const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
-  switch (RelocationSection->sh_type) {
-  default:
-    llvm_unreachable("unexpected relocation section type");
-  case ELF::SHT_REL:
-    Symbol = Obj->getRel(Rel)->getSymbol(EF.isMips64EL());
-    break;
-  case ELF::SHT_RELA:
-    Symbol = Obj->getRela(Rel)->getSymbol(EF.isMips64EL());
-    break;
-  }
-
-  return Symbol;
-}
-
-uint32_t getRelocationSymbol(const ELFObjectFileBase *Obj,
-                             const RelocationRef &Rel) {
-  return getRelocationSymbol(cast<ELF64LEObjectFile>(Obj), Rel);
-}
-} // anonymous namespace
-
 bool RewriteInstance::analyzeRelocation(
     const RelocationRef &Rel, uint64_t &RType, std::string &SymbolName,
     bool &IsSectionRelocation, uint64_t &SymbolAddress, int64_t &Addend,
@@ -2196,7 +2138,7 @@ bool RewriteInstance::analyzeRelocation(
     return true;
 
   ExtractedValue = Relocation::extractValue(RType, *Value, Rel.getOffset());
-  Addend = getRelocationAddend(InputFile, Rel);
+  Addend = BinaryContext::getRelocationAddend(InputFile, Rel);
 
   const bool IsPCRelative = Relocation::isPCRelative(RType);
   const uint64_t PCRelOffset = IsPCRelative && !IsAArch64 ? Rel.getOffset() : 0;
@@ -2396,7 +2338,7 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
     StringRef SymbolName = "<none>";
     MCSymbol *Symbol = nullptr;
     uint64_t SymbolAddress = 0;
-    const uint64_t Addend = getRelocationAddend(InputFile, Rel);
+    const uint64_t Addend = BinaryContext::getRelocationAddend(InputFile, Rel);
 
     symbol_iterator SymbolIter = Rel.getSymbol();
     if (SymbolIter != InputFile->symbol_end()) {
@@ -2421,7 +2363,7 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
       IsJmpRelocation[RType] = true;
 
     if (Symbol)
-      SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
+      SymbolIndex[Symbol] = BinaryContext::getRelocationSymbol(InputFile, Rel);
 
     BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend);
   }
diff --git a/bolt/test/X86/Inputs/helperSafeICF.s b/bolt/test/X86/Inputs/helperSafeICF.s
new file mode 100644
index 00000000000000..8ab47324454cbc
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICF.s
@@ -0,0 +1,39 @@
+# clang++ -c helper.cpp -o helper.o
+# int FooVar = 1;
+# int BarVar = 2;
+#
+# int fooGlobalFuncHelper(int a, int b) {
+#   return 5;
+# }
+	.text
+	.file	"helper.cpp"
+	.globl	_Z19fooGlobalFuncHelperii       # -- Begin function _Z19fooGlobalFuncHelperii
+	.p2align	4, 0x90
+	.type	_Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii:              # @_Z19fooGlobalFuncHelperii
+	.cfi_startproc
+# %bb.0:
+	movl	$5, %eax
+	retq
+.Lfunc_end0:
+	.size	_Z19fooGlobalFuncHelperii, .Lfunc_end0-_Z19fooGlobalFuncHelperii
+	.cfi_endproc
+                                        # -- End function
+	.type	FooVar, at object                  # @FooVar
+	.data
+	.globl	FooVar
+	.p2align	2, 0x0
+FooVar:
+	.long	1                               # 0x1
+	.size	FooVar, 4
+
+	.type	BarVar, at object                  # @BarVar
+	.globl	BarVar
+	.p2align	2, 0x0
+BarVar:
+	.long	2                               # 0x2
+	.size	BarVar, 4
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
diff --git a/bolt/test/X86/Inputs/helperSafeICFICPTest.s b/bolt/test/X86/Inputs/helperSafeICFICPTest.s
new file mode 100644
index 00000000000000..ae62b4f292656f
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFICPTest.s
@@ -0,0 +1,40 @@
+# clang++ -O2 helper.cpp -c -o helperProf.o
+# int returnFive() {
+#   return 5;
+# }
+# int returnFourOrFive(int val) {
+#   return val == 1 ? 4 : 5;
+# }
+
+	.text
+	.file	"helper.cpp"
+	.globl	_Z10returnFivev                 # -- Begin function _Z10returnFivev
+	.p2align	4, 0x90
+	.type	_Z10returnFivev, at function
+_Z10returnFivev:                        # @_Z10returnFivev
+	.cfi_startproc
+# %bb.0:                                # %entry
+	movl	$5, %eax
+	retq
+.Lfunc_end0:
+	.size	_Z10returnFivev, .Lfunc_end0-_Z10returnFivev
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z16returnFourOrFivei           # -- Begin function _Z16returnFourOrFivei
+	.p2align	4, 0x90
+	.type	_Z16returnFourOrFivei, at function
+_Z16returnFourOrFivei:                  # @_Z16returnFourOrFivei
+	.cfi_startproc
+# %bb.0:                                # %entry
+	xorl	%eax, %eax
+	cmpl	$1, %edi
+	sete	%al
+	xorl	$5, %eax
+	retq
+.Lfunc_end1:
+	.size	_Z16returnFourOrFivei, .Lfunc_end1-_Z16returnFourOrFivei
+	.cfi_endproc
+                                        # -- End function
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
diff --git a/bolt/test/X86/Inputs/mainSafeICFICPTest.s b/bolt/test/X86/Inputs/mainSafeICFICPTest.s
new file mode 100644
index 00000000000000..4b12acdeae0995
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFICPTest.s
@@ -0,0 +1,331 @@
+# generate profile
+# clang++ -O2 -fprofile-generate=. main.cpp   -c -o mainProf.o
+# PROF=test.profdata
+# clang++ -m64  -fprofile-use=$PROF \
+#   -mllvm -disable-icp=true -mllvm -print-after-all \
+#   -g0 -flto=thin -fwhole-program-vtables -fno-split-lto-unit -O2 \
+#   -fdebug-types-section \
+#   main.cpp -c -o mainProfLTO.bc
+# PASS='pgo-icall-prom'
+# clang++ -m64  -fprofile-use=$PROF \
+#   -O3 -Rpass=$PASS \
+#   -mllvm -print-before=$PASS \
+#   -mllvm -print-after=$PASS \
+#   -mllvm -filter-print-funcs=main \
+#   -mllvm -debug-only=$PASS \
+#   -x ir \
+#   mainProfLTO.bc -c -o mainProfFinal.o
+
+# class Base {
+# public:
+#   virtual int func(int a, int b) const = 0;
+#
+#   virtual ~Base() {};
+# };
+#
+# //namespace {
+# class Derived2 : public Base {
+#   int c = 5;
+# public:
+#   __attribute__((noinline)) int func(int a, int b)const override { return a * (a - b) + this->c; }
+#
+#   ~Derived2() {}
+# };
+#
+# class Derived3 : public Base {
+#   int c = 500;
+# public:
+#   __attribute__((noinline)) int func(int a, int b) const override { return a * (a - b) + this->c; }
+#   ~Derived3() {}
+# };
+# //} // namespace//
+#
+# __attribute__((noinline)) Base *createType(int a) {
+#     Base *base = nullptr;
+#     if (a == 4)
+#       base = new Derived2();
+#     else
+#       base = new Derived3();
+#     return base;
+# }
+#
+# extern int returnFive();
+# extern int returnFourOrFive(int val);
+# int main(int argc, char **argv) {
+#   int sum = 0;
+#   int a = returnFourOrFive(argc);
+#   int b = returnFive();
+#   Base *ptr = createType(a);
+#   Base *ptr2 = createType(b);
+#   sum += ptr->func(b, a) + ptr2->func(b, a);
+#   return 0;
+# }
+  .text
+	.file	"main.cpp"
+	.section	.text.hot.,"ax", at progbits
+	.globl	_Z10createTypei                 # -- Begin function _Z10createTypei
+	.p2align	4, 0x90
+	.type	_Z10createTypei, at function
+_Z10createTypei:                        # @_Z10createTypei
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbx
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbx, -16
+	movl	%edi, %ebx
+	movl	$16, %edi
+	callq	_Znwm at PLT
+	cmpl	$4, %ebx
+	xorps	%xmm0, %xmm0
+	leaq	_ZTV8Derived2+16(%rip), %rcx
+	leaq	_ZTV8Derived3+16(%rip), %rdx
+	cmoveq	%rcx, %rdx
+	movl	$5, %ecx
+	movl	$500, %esi                      # imm = 0x1F4
+	cmovel	%ecx, %esi
+	movaps	%xmm0, (%rax)
+	movq	%rdx, (%rax)
+	movl	%esi, 8(%rax)
+	popq	%rbx
+	.cfi_def_cfa_offset 8
+	retq
+.Lfunc_end0:
+	.size	_Z10createTypei, .Lfunc_end0-_Z10createTypei
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	pushq	%r15
+	.cfi_def_cfa_offset 24
+	pushq	%r14
+	.cfi_def_cfa_offset 32
+	pushq	%rbx
+	.cfi_def_cfa_offset 40
+	pushq	%rax
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbx, -40
+	.cfi_offset %r14, -32
+	.cfi_offset %r15, -24
+	.cfi_offset %rbp, -16
+	callq	_Z16returnFourOrFivei at PLT
+	movl	%eax, %ebx
+	callq	_Z10returnFivev at PLT
+	movl	%eax, %ebp
+	movl	%ebx, %edi
+	callq	_Z10createTypei
+	movq	%rax, %r15
+	movl	%ebp, %edi
+	callq	_Z10createTypei
+	movq	%rax, %r14
+	movq	(%r15), %rax
+	movq	(%rax), %rax
+	leaq	_ZNK8Derived24funcEii(%rip), %rcx
+	movq	%r15, %rdi
+	movl	%ebp, %esi
+	movl	%ebx, %edx
+	cmpq	%rcx, %rax
+	jne	.LBB1_2
+# %bb.1:                                # %if.true.direct_targ
+	callq	_ZNK8Derived24funcEii
+.LBB1_3:                                # %if.end.icp
+	movq	(%r14), %rax
+	movq	(%rax), %rax
+	leaq	_ZNK8Derived34funcEii(%rip), %rcx
+	movq	%r14, %rdi
+	movl	%ebp, %esi
+	movl	%ebx, %edx
+	cmpq	%rcx, %rax
+	jne	.LBB1_5
+# %bb.4:                                # %if.true.direct_targ1
+	callq	_ZNK8Derived34funcEii
+.LBB1_6:                                # %if.end.icp3
+	xorl	%eax, %eax
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 40
+	popq	%rbx
+	.cfi_def_cfa_offset 32
+	popq	%r14
+	.cfi_def_cfa_offset 24
+	popq	%r15
+	.cfi_def_cfa_offset 16
+	popq	%rbp
+	.cfi_def_cfa_offset 8
+	retq
+.LBB1_2:                                # %if.false.orig_indirect
+	.cfi_def_cfa_offset 48
+	callq	*%rax
+	jmp	.LBB1_3
+.LBB1_5:                                # %if.false.orig_indirect2
+	callq	*%rax
+	jmp	.LBB1_6
+.Lfunc_end1:
+	.size	main, .Lfunc_end1-main
+	.cfi_endproc
+                                        # -- End function
+	.section	.text.hot._ZNK8Derived24funcEii,"axG", at progbits,_ZNK8Derived24funcEii,comdat
+	.weak	_ZNK8Derived24funcEii           # -- Begin function _ZNK8Derived24funcEii
+	.p2align	4, 0x90
+	.type	_ZNK8Derived24funcEii, at function
+_ZNK8Derived24funcEii:                  # @_ZNK8Derived24funcEii
+	.cfi_startproc
+# %bb.0:                                # %entry
+	movl	%esi, %eax
+	subl	%edx, %eax
+	imull	%esi, %eax
+	addl	8(%rdi), %eax
+	retq
+.Lfunc_end2:
+	.size	_ZNK8Derived24funcEii, .Lfunc_end2-_ZNK8Derived24funcEii
+	.cfi_endproc
+                                        # -- End function
+	.section	.text.unlikely._ZN8Derived2D0Ev,"axG", at progbits,_ZN8Derived2D0Ev,comdat
+	.weak	_ZN8Derived2D0Ev                # -- Begin function _ZN8Derived2D0Ev
+	.p2align	4, 0x90
+	.type	_ZN8Derived2D0Ev, at function
+_ZN8Derived2D0Ev:                       # @_ZN8Derived2D0Ev
+	.cfi_startproc
+# %bb.0:                                # %entry
+	movl	$16, %esi
+	jmp	_ZdlPvm at PLT                     # TAILCALL
+.Lfunc_end3:
+	.size	_ZN8Derived2D0Ev, .Lfunc_end3-_ZN8Derived2D0Ev
+	.cfi_endproc
+                                        # -- End function
+	.section	.text.hot._ZNK8Derived34funcEii,"axG", at progbits,_ZNK8Derived34funcEii,comdat
+	.weak	_ZNK8Derived34funcEii           # -- Begin function _ZNK8Derived34funcEii
+	.p2align	4, 0x90
+	.type	_ZNK8Derived34funcEii, at function
+_ZNK8Derived34funcEii:                  # @_ZNK8Derived34funcEii
+	.cfi_startproc
+# %bb.0:                                # %entry
+	movl	%esi, %eax
+	subl	%edx, %eax
+	imull	%esi, %eax
+	addl	8(%rdi), %eax
+	retq
+.Lfunc_end4:
+	.size	_ZNK8Derived34funcEii, .Lfunc_end4-_ZNK8Derived34funcEii
+	.cfi_endproc
+                                        # -- End function
+	.section	.text.unlikely._ZN4BaseD2Ev,"axG", at progbits,_ZN4BaseD2Ev,comdat
+	.weak	_ZN4BaseD2Ev                    # -- Begin function _ZN4BaseD2Ev
+	.p2align	4, 0x90
+	.type	_ZN4BaseD2Ev, at function
+_ZN4BaseD2Ev:                           # @_ZN4BaseD2Ev
+	.cfi_startproc
+# %bb.0:                                # %entry
+	retq
+.Lfunc_end5:
+	.size	_ZN4BaseD2Ev, .Lfunc_end5-_ZN4BaseD2Ev
+	.cfi_endproc
+                                        # -- End function
+	.section	.text.unlikely._ZN8Derived3D0Ev,"axG", at progbits,_ZN8Derived3D0Ev,comdat
+	.weak	_ZN8Derived3D0Ev                # -- Begin function _ZN8Derived3D0Ev
+	.p2align	4, 0x90
+	.type	_ZN8Derived3D0Ev, at function
+_ZN8Derived3D0Ev:                       # @_ZN8Derived3D0Ev
+	.cfi_startproc
+# %bb.0:                                # %entry
+	movl	$16, %esi
+	jmp	_ZdlPvm at PLT                     # TAILCALL
+.Lfunc_end6:
+	.size	_ZN8Derived3D0Ev, .Lfunc_end6-_ZN8Derived3D0Ev
+	.cfi_endproc
+                                        # -- End function
+	.type	_ZTV8Derived2, at object           # @_ZTV8Derived2
+	.section	.data.rel.ro._ZTV8Derived2,"awG", at progbits,_ZTV8Derived2,comdat
+	.weak	_ZTV8Derived2
+	.p2align	3, 0x0
+_ZTV8Derived2:
+	.quad	0
+	.quad	_ZTI8Derived2
+	.quad	_ZNK8Derived24funcEii
+	.quad	_ZN4BaseD2Ev
+	.quad	_ZN8Derived2D0Ev
+	.size	_ZTV8Derived2, 40
+
+	.type	_ZTS8Derived2, at object           # @_ZTS8Derived2
+	.section	.rodata._ZTS8Derived2,"aG", at progbits,_ZTS8Derived2,comdat
+	.weak	_ZTS8Derived2
+_ZTS8Derived2:
+	.asciz	"8Derived2"
+	.size	_ZTS8Derived2, 10
+
+	.type	_ZTS4Base, at object               # @_ZTS4Base
+	.section	.rodata._ZTS4Base,"aG", at progbits,_ZTS4Base,comdat
+	.weak	_ZTS4Base
+_ZTS4Base:
+	.asciz	"4Base"
+	.size	_ZTS4Base, 6
+
+	.type	_ZTI4Base, at object               # @_ZTI4Base
+	.section	.data.rel.ro._ZTI4Base,"awG", at progbits,_ZTI4Base,comdat
+	.weak	_ZTI4Base
+	.p2align	3, 0x0
+_ZTI4Base:
+	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
+	.quad	_ZTS4Base
+	.size	_ZTI4Base, 16
+
+	.type	_ZTI8Derived2, at object           # @_ZTI8Derived2
+	.section	.data.rel.ro._ZTI8Derived2,"awG", at progbits,_ZTI8Derived2,comdat
+	.weak	_ZTI8Derived2
+	.p2align	3, 0x0
+_ZTI8Derived2:
+	.quad	_ZTVN10__cxxabiv120__si_class_type_infoE+16
+	.quad	_ZTS8Derived2
+	.quad	_ZTI4Base
+	.size	_ZTI8Derived2, 24
+
+	.type	_ZTV8Derived3, at object           # @_ZTV8Derived3
+	.section	.data.rel.ro._ZTV8Derived3,"awG", at progbits,_ZTV8Derived3,comdat
+	.weak	_ZTV8Derived3
+	.p2align	3, 0x0
+_ZTV8Derived3:
+	.quad	0
+	.quad	_ZTI8Derived3
+	.quad	_ZNK8Derived34funcEii
+	.quad	_ZN4BaseD2Ev
+	.quad	_ZN8Derived3D0Ev
+	.size	_ZTV8Derived3, 40
+
+	.type	_ZTS8Derived3, at object           # @_ZTS8Derived3
+	.section	.rodata._ZTS8Derived3,"aG", at progbits,_ZTS8Derived3,comdat
+	.weak	_ZTS8Derived3
+_ZTS8Derived3:
+	.asciz	"8Derived3"
+	.size	_ZTS8Derived3, 10
+
+	.type	_ZTI8Derived3, at object           # @_ZTI8Derived3
+	.section	.data.rel.ro._ZTI8Derived3,"awG", at progbits,_ZTI8Derived3,comdat
+	.weak	_ZTI8Derived3
+	.p2align	3, 0x0
+_ZTI8Derived3:
+	.quad	_ZTVN10__cxxabiv120__si_class_type_infoE+16
+	.quad	_ZTS8Derived3
+	.quad	_ZTI4Base
+	.size	_ZTI8Derived3, 24
+
+	.cg_profile _Z10createTypei, _Znwm, 2
+	.cg_profile main, _Z16returnFourOrFivei, 1
+	.cg_profile main, _Z10returnFivev, 1
+	.cg_profile main, _Z10createTypei, 2
+	.cg_profile main, _ZNK8Derived24funcEii, 1
+	.cg_profile main, _ZNK8Derived34funcEii, 1
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _ZTVN10__cxxabiv120__si_class_type_infoE
+	.addrsig_sym _ZTS8Derived2
+	.addrsig_sym _ZTVN10__cxxabiv117__class_type_infoE
+	.addrsig_sym _ZTS4Base
+	.addrsig_sym _ZTI4Base
+	.addrsig_sym _ZTI8Derived2
+	.addrsig_sym _ZTS8Derived3
+	.addrsig_sym _ZTI8Derived3
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest1.s b/bolt/test/X86/Inputs/mainSafeICFTest1.s
new file mode 100644
index 00000000000000..7d38f0bd190fd8
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest1.s
@@ -0,0 +1,345 @@
+# clang++ -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# int main(int argc, char **argv) {
+#   int temp = helper1(barAdd, FooVar, BarVar) +
+#              helper2(fooMul, barMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   return temp;
+# }
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFiiiEii              # -- Begin function _Z7helper1PFiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii:                     # @_Z7helper1PFiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	leaq	_Z6barAddii(%rip), %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFiiiES0_ii           # -- Begin function _Z7helper2PFiiiES0_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii:                  # @_Z7helper2PFiiiES0_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	leaq	_Z6barAddii(%rip), %rdi
+	callq	_Z7helper1PFiiiEii
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	leaq	_Z6barMulii(%rip), %rsi
+	callq	_Z7helper2PFiiiES0_ii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-28(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -24(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-24(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -20(%rbp)
+	movl	-20(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFiiiEii
+	.addrsig_sym _Z7helper2PFiiiES0_ii
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s
new file mode 100644
index 00000000000000..5d5d00c12320be
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO0.s
@@ -0,0 +1,363 @@
+# clang++ -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# static int (*funcGlobalBarAdd)(int, int) = barAdd;
+# int (*funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   return temp;
+# }
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFiiiEii              # -- Begin function _Z7helper1PFiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii:                     # @_Z7helper1PFiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	leaq	_Z6barAddii(%rip), %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFiiiES0_ii           # -- Begin function _Z7helper2PFiiiES0_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii:                  # @_Z7helper2PFiiiES0_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	movq	_ZL16funcGlobalBarAdd(%rip), %rdi
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	callq	_Z7helper1PFiiiEii
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	funcGlobalBarMul(%rip), %rsi
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	callq	_Z7helper2PFiiiES0_ii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-28(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -24(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-24(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -20(%rbp)
+	movl	-20(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.type	funcGlobalBarMul, at object        # @funcGlobalBarMul
+	.data
+	.globl	funcGlobalBarMul
+	.p2align	3, 0x0
+funcGlobalBarMul:
+	.quad	_Z6barMulii
+	.size	funcGlobalBarMul, 8
+
+	.type	_ZL16funcGlobalBarAdd, at object   # @_ZL16funcGlobalBarAdd
+	.p2align	3, 0x0
+_ZL16funcGlobalBarAdd:
+	.quad	_Z6barAddii
+	.size	_ZL16funcGlobalBarAdd, 8
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFiiiEii
+	.addrsig_sym _Z7helper2PFiiiES0_ii
+	.addrsig_sym funcGlobalBarMul
+	.addrsig_sym _ZL16funcGlobalBarAdd
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s
new file mode 100644
index 00000000000000..3579fa2eae293b
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest2GlobalVarO3.s
@@ -0,0 +1,293 @@
+# clang++ -O3 -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# static int (*funcGlobalBarAdd)(int, int) = barAdd;
+# int (*funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   return temp;
+# }
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	subl	%esi, %eax
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	subl	%esi, %eax
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	imull	%esi, %eax
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	imull	%esi, %eax
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+                                        # kill: def $esi killed $esi def $rsi
+                                        # kill: def $edi killed $edi def $rdi
+	leal	(%rdi,%rsi), %eax
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+                                        # kill: def $esi killed $esi def $rsi
+                                        # kill: def $edi killed $edi def $rdi
+	leal	(%rdi,%rsi), %eax
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFiiiEii              # -- Begin function _Z7helper1PFiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii:                     # @_Z7helper1PFiiiEii
+	.cfi_startproc
+# %bb.0:
+	leaq	_Z6barAddii(%rip), %rcx
+	cmpq	%rcx, %rdi
+	je	.LBB6_1
+# %bb.2:
+	pushq	%rax
+	.cfi_def_cfa_offset 16
+	movq	%rdi, %rax
+	movl	%esi, %edi
+	movl	%edx, %esi
+	callq	*%rax
+	addl	$-4, %eax
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 8
+	retq
+.LBB6_1:
+	movl	$1, %eax
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFiiiES0_ii           # -- Begin function _Z7helper2PFiiiES0_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii:                  # @_Z7helper2PFiiiES0_ii
+	.cfi_startproc
+# %bb.0:
+	cmpq	%rsi, %rdi
+	je	.LBB7_1
+# %bb.2:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	pushq	%r15
+	.cfi_def_cfa_offset 24
+	pushq	%r14
+	.cfi_def_cfa_offset 32
+	pushq	%rbx
+	.cfi_def_cfa_offset 40
+	pushq	%rax
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbx, -40
+	.cfi_offset %r14, -32
+	.cfi_offset %r15, -24
+	.cfi_offset %rbp, -16
+	movl	%ecx, %ebx
+	movl	%edx, %ebp
+	movq	%rsi, %r14
+	movq	%rdi, %rax
+	movl	%edx, %edi
+	movl	%ecx, %esi
+	callq	*%rax
+	movl	%eax, %r15d
+	movl	%ebp, %edi
+	movl	%ebx, %esi
+	callq	*%r14
+	addl	%r15d, %eax
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 40
+	popq	%rbx
+	.cfi_def_cfa_offset 32
+	popq	%r14
+	.cfi_def_cfa_offset 24
+	popq	%r15
+	.cfi_def_cfa_offset 16
+	popq	%rbp
+	.cfi_def_cfa_offset 8
+	.cfi_restore %rbx
+	.cfi_restore %r14
+	.cfi_restore %r15
+	.cfi_restore %rbp
+	retq
+.LBB7_1:
+	movl	$2, %eax
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	pushq	%r15
+	.cfi_def_cfa_offset 24
+	pushq	%r14
+	.cfi_def_cfa_offset 32
+	pushq	%r12
+	.cfi_def_cfa_offset 40
+	pushq	%rbx
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbx, -48
+	.cfi_offset %r12, -40
+	.cfi_offset %r14, -32
+	.cfi_offset %r15, -24
+	.cfi_offset %rbp, -16
+	movq	FooVar at GOTPCREL(%rip), %r14
+	movl	(%r14), %esi
+	movq	BarVar at GOTPCREL(%rip), %r15
+	movl	(%r15), %edx
+	leaq	_Z6barAddii(%rip), %rdi
+	callq	_Z7helper1PFiiiEii
+	movl	%eax, %ebx
+	movq	funcGlobalBarMul(%rip), %rsi
+	movl	(%r14), %edx
+	movl	(%r15), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	callq	_Z7helper2PFiiiES0_ii
+	movl	%eax, %ebp
+	addl	%ebx, %ebp
+	movl	(%r14), %ebx
+	movl	(%r15), %r14d
+	movl	%ebx, %edi
+	movl	%r14d, %esi
+	callq	_Z6fooSubii
+	movl	%eax, %r15d
+	movl	%ebx, %edi
+	movl	%r14d, %esi
+	callq	_Z6barSubii
+	movl	%eax, %r12d
+	addl	%r15d, %r12d
+	addl	%ebp, %r12d
+	movl	%ebx, %edi
+	movl	%r14d, %esi
+	callq	_Z6fooAddii
+	addl	%r12d, %eax
+	popq	%rbx
+	.cfi_def_cfa_offset 40
+	popq	%r12
+	.cfi_def_cfa_offset 32
+	popq	%r14
+	.cfi_def_cfa_offset 24
+	popq	%r15
+	.cfi_def_cfa_offset 16
+	popq	%rbp
+	.cfi_def_cfa_offset 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.type	funcGlobalBarMul, at object        # @funcGlobalBarMul
+	.data
+	.globl	funcGlobalBarMul
+	.p2align	3, 0x0
+funcGlobalBarMul:
+	.quad	_Z6barMulii
+	.size	funcGlobalBarMul, 8
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6barAddii
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s
new file mode 100644
index 00000000000000..56036d1b14e2b9
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO0.s
@@ -0,0 +1,359 @@
+
+# clang++ -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# int main(int argc, char **argv) {
+#   static int (*funcGlobalBarAdd)(int, int) = barAdd;
+#   int (*funcGlobalBarMul)(int, int) = barMul;
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   MY_PRINTF("val: %d", temp);
+#   return temp;
+# }
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFiiiEii              # -- Begin function _Z7helper1PFiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii:                     # @_Z7helper1PFiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	leaq	_Z6barAddii(%rip), %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFiiiES0_ii           # -- Begin function _Z7helper2PFiiiES0_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii:                  # @_Z7helper2PFiiiES0_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	leaq	_Z6barMulii(%rip), %rax
+	movq	%rax, -24(%rbp)
+	movq	_ZZ4mainE16funcGlobalBarAdd(%rip), %rdi
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	callq	_Z7helper1PFiiiEii
+	movl	%eax, -44(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rsi
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	callq	_Z7helper2PFiiiES0_ii
+	movl	%eax, %ecx
+	movl	-44(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -40(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-40(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)
+	movl	-28(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.type	_ZZ4mainE16funcGlobalBarAdd, at object # @_ZZ4mainE16funcGlobalBarAdd
+	.data
+	.p2align	3, 0x0
+_ZZ4mainE16funcGlobalBarAdd:
+	.quad	_Z6barAddii
+	.size	_ZZ4mainE16funcGlobalBarAdd, 8
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFiiiEii
+	.addrsig_sym _Z7helper2PFiiiES0_ii
+	.addrsig_sym _ZZ4mainE16funcGlobalBarAdd
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s
new file mode 100644
index 00000000000000..aedad906912654
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFTest3LocalVarO3.s
@@ -0,0 +1,286 @@
+# clang++ -O3 -c main.cpp -o main.o
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# int helper1(int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# int helper2(int (*func)(int, int), int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# int main(int argc, char **argv) {
+#   static int (*funcGlobalBarAdd)(int, int) = barAdd;
+#   int (*funcGlobalBarMul)(int, int) = barMul;
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   MY_PRINTF("val: %d", temp);
+#   return temp;
+# }
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	subl	%esi, %eax
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	subl	%esi, %eax
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	imull	%esi, %eax
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	movl	%edi, %eax
+	imull	%esi, %eax
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+                                        # kill: def $esi killed $esi def $rsi
+                                        # kill: def $edi killed $edi def $rdi
+	leal	(%rdi,%rsi), %eax
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+                                        # kill: def $esi killed $esi def $rsi
+                                        # kill: def $edi killed $edi def $rdi
+	leal	(%rdi,%rsi), %eax
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFiiiEii              # -- Begin function _Z7helper1PFiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFiiiEii, at function
+_Z7helper1PFiiiEii:                     # @_Z7helper1PFiiiEii
+	.cfi_startproc
+# %bb.0:
+	leaq	_Z6barAddii(%rip), %rcx
+	cmpq	%rcx, %rdi
+	je	.LBB6_1
+# %bb.2:
+	pushq	%rax
+	.cfi_def_cfa_offset 16
+	movq	%rdi, %rax
+	movl	%esi, %edi
+	movl	%edx, %esi
+	callq	*%rax
+	addl	$-4, %eax
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 8
+	retq
+.LBB6_1:
+	movl	$1, %eax
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFiiiEii, .Lfunc_end6-_Z7helper1PFiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFiiiES0_ii           # -- Begin function _Z7helper2PFiiiES0_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFiiiES0_ii, at function
+_Z7helper2PFiiiES0_ii:                  # @_Z7helper2PFiiiES0_ii
+	.cfi_startproc
+# %bb.0:
+	cmpq	%rsi, %rdi
+	je	.LBB7_1
+# %bb.2:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	pushq	%r15
+	.cfi_def_cfa_offset 24
+	pushq	%r14
+	.cfi_def_cfa_offset 32
+	pushq	%rbx
+	.cfi_def_cfa_offset 40
+	pushq	%rax
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbx, -40
+	.cfi_offset %r14, -32
+	.cfi_offset %r15, -24
+	.cfi_offset %rbp, -16
+	movl	%ecx, %ebx
+	movl	%edx, %ebp
+	movq	%rsi, %r14
+	movq	%rdi, %rax
+	movl	%edx, %edi
+	movl	%ecx, %esi
+	callq	*%rax
+	movl	%eax, %r15d
+	movl	%ebp, %edi
+	movl	%ebx, %esi
+	callq	*%r14
+	addl	%r15d, %eax
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 40
+	popq	%rbx
+	.cfi_def_cfa_offset 32
+	popq	%r14
+	.cfi_def_cfa_offset 24
+	popq	%r15
+	.cfi_def_cfa_offset 16
+	popq	%rbp
+	.cfi_def_cfa_offset 8
+	.cfi_restore %rbx
+	.cfi_restore %r14
+	.cfi_restore %r15
+	.cfi_restore %rbp
+	retq
+.LBB7_1:
+	movl	$2, %eax
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFiiiES0_ii, .Lfunc_end7-_Z7helper2PFiiiES0_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	pushq	%r15
+	.cfi_def_cfa_offset 24
+	pushq	%r14
+	.cfi_def_cfa_offset 32
+	pushq	%r12
+	.cfi_def_cfa_offset 40
+	pushq	%rbx
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbx, -48
+	.cfi_offset %r12, -40
+	.cfi_offset %r14, -32
+	.cfi_offset %r15, -24
+	.cfi_offset %rbp, -16
+	movq	FooVar at GOTPCREL(%rip), %r14
+	movl	(%r14), %esi
+	movq	BarVar at GOTPCREL(%rip), %r15
+	movl	(%r15), %edx
+	leaq	_Z6barAddii(%rip), %rdi
+	callq	_Z7helper1PFiiiEii
+	movl	%eax, %ebx
+	movl	(%r14), %edx
+	movl	(%r15), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	leaq	_Z6barMulii(%rip), %rsi
+	callq	_Z7helper2PFiiiES0_ii
+	movl	%eax, %ebp
+	addl	%ebx, %ebp
+	movl	(%r14), %ebx
+	movl	(%r15), %r14d
+	movl	%ebx, %edi
+	movl	%r14d, %esi
+	callq	_Z6fooSubii
+	movl	%eax, %r15d
+	movl	%ebx, %edi
+	movl	%r14d, %esi
+	callq	_Z6barSubii
+	movl	%eax, %r12d
+	addl	%r15d, %r12d
+	addl	%ebp, %r12d
+	movl	%ebx, %edi
+	movl	%r14d, %esi
+	callq	_Z6fooAddii
+	addl	%r12d, %eax
+	popq	%rbx
+	.cfi_def_cfa_offset 40
+	popq	%r12
+	.cfi_def_cfa_offset 32
+	popq	%r14
+	.cfi_def_cfa_offset 24
+	popq	%r15
+	.cfi_def_cfa_offset 16
+	popq	%rbp
+	.cfi_def_cfa_offset 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6barAddii
diff --git a/bolt/test/X86/icf-safe-icp.test b/bolt/test/X86/icf-safe-icp.test
new file mode 100644
index 00000000000000..efeaf469eedeea
--- /dev/null
+++ b/bolt/test/X86/icf-safe-icp.test
@@ -0,0 +1,20 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+# The compare is generated by the ICP path with instrumentation profiling.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFICPTest.s    -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFICPTest.s  -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf      -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _ZN8Derived3D0Ev into _ZN8Derived2D0Ev
+# ICFCHECK-NEXT: folding _ZNK8Derived34funcEii into _ZNK8Derived24funcEii
+
+# SAFEICFCHECK:      skipping function _ZNK8Derived24funcEii
+# SAFEICFCHECK-NEXT: skipping function _ZNK8Derived34funcEii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _ZN8Derived3D0Ev into _ZN8Derived2D0Ev
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test1-no-cfg.test b/bolt/test/X86/icf-safe-test1-no-cfg.test
new file mode 100644
index 00000000000000..37c5debe8c814b
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1-no-cfg.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf      -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf --skip-funcs=_Z7helper1PFiiiEii,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test1-no-relocs.test b/bolt/test/X86/icf-safe-test1-no-relocs.test
new file mode 100644
index 00000000000000..3b5d7400d85115
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1-no-relocs.test
@@ -0,0 +1,16 @@
+## Checks that BOLT handles correctly with no relocations with --safe-icf option.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: not llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK: BOLT-ERROR: Binary built without relocations. Safe ICF is not supported
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
new file mode 100644
index 00000000000000..2b41633d07bf93
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO0.test b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
new file mode 100644
index 00000000000000..904d792e225b9c
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest2GlobalVarO0.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO3.test b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
new file mode 100644
index 00000000000000..288faf252ad745
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest2GlobalVarO3.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO0.test b/bolt/test/X86/icf-safe-test3LocalVarO0.test
new file mode 100644
index 00000000000000..56a329b2f7a598
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test3LocalVarO0.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest3LocalVarO0.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO3.test b/bolt/test/X86/icf-safe-test3LocalVarO3.test
new file mode 100644
index 00000000000000..d0782e46b17e06
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test3LocalVarO3.test
@@ -0,0 +1,21 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest3LocalVarO3.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------

>From 237c02e48825e2e42ec7cee62ef4a83ad0a8c4b1 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Thu, 14 Nov 2024 17:38:10 -0800
Subject: [PATCH 2/6] Changed icf option

---
 bolt/include/bolt/Core/BinaryContext.h        | 11 ++++++++++
 .../bolt/Passes/IdenticalCodeFolding.h        |  6 ++----
 bolt/lib/Core/BinaryContext.cpp               | 21 ++++++++++++-------
 bolt/lib/Passes/IdenticalCodeFolding.cpp      |  2 +-
 bolt/lib/Rewrite/BinaryPassManager.cpp        | 12 ++++-------
 bolt/lib/Rewrite/BoltDiff.cpp                 |  4 ++--
 bolt/test/X86/icf-safe-icp.test               |  2 +-
 bolt/test/X86/icf-safe-test1-no-cfg.test      |  4 ++--
 bolt/test/X86/icf-safe-test1-no-relocs.test   |  4 ++--
 bolt/test/X86/icf-safe-test1.test             |  2 +-
 bolt/test/X86/icf-safe-test2GlobalVarO0.test  |  2 +-
 bolt/test/X86/icf-safe-test2GlobalVarO3.test  |  2 +-
 bolt/test/X86/icf-safe-test3LocalVarO0.test   |  2 +-
 bolt/test/X86/icf-safe-test3LocalVarO3.test   |  2 +-
 14 files changed, 44 insertions(+), 32 deletions(-)

diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 92889ffe162ae1..3e58ec502acd09 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -276,6 +276,12 @@ class BinaryContext {
   void deregisterSectionName(const BinarySection &Section);
 
 public:
+  enum class ICFLevel {
+    None,
+    Safe, // Safe ICF for all sections.
+    All,  // Aggressive ICF for code, but safe ICF for data, similar to MSVC's
+          // behavior.
+  };
   static Expected<std::unique_ptr<BinaryContext>>
   createBinaryContext(Triple TheTriple, StringRef InputFileName,
                       SubtargetFeatures *Features, bool IsPIC,
@@ -666,6 +672,9 @@ class BinaryContext {
 
   std::unique_ptr<MCAsmBackend> MAB;
 
+  /// ICF level to use for this binary.
+  ICFLevel ICFLevelVar{ICFLevel::None};
+
   /// Allows BOLT to print to log whenever it is necessary (with or without
   /// const references)
   mutable JournalingStreams Logger;
@@ -1485,6 +1494,8 @@ class BinaryContext {
     return *IOAddressMap;
   }
 
+  ICFLevel getICFLevel() const { return ICFLevelVar; }
+
   raw_ostream &outs() const { return Logger.Out; }
 
   raw_ostream &errs() const { return Logger.Err; }
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index 75d5f19ddfc7be..41fb17408d5876 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -31,8 +31,8 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
   }
 
 public:
-  explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass, bool IsSafeICF)
-      : BinaryFunctionPass(PrintPass), IsSafeICF(IsSafeICF) {}
+  explicit IdenticalCodeFolding(const cl::opt<bool> &PrintPass)
+      : BinaryFunctionPass(PrintPass) {}
 
   const char *getName() const override { return "identical-code-folding"; }
   Error runOnFunctions(BinaryContext &BC) override;
@@ -44,8 +44,6 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
   /// references.
   void processDataRelocations(BinaryContext &BC,
                               const SectionRef &SecRefRelData);
-
-  bool IsSafeICF;
 };
 
 } // namespace bolt
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 31352bed6fdab7..6cf1562735eb45 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -76,12 +76,8 @@ cl::opt<std::string> CompDirOverride(
              "to *.dwo files."),
     cl::Hidden, cl::init(""), cl::cat(BoltCategory));
 
-cl::opt<bool> ICF("icf", cl::desc("fold functions with identical code"),
-                  cl::cat(BoltOptCategory));
-
-cl::opt<bool> SafeICF("safe-icf",
-                      cl::desc("Enable safe identical code folding"),
-                      cl::cat(BoltOptCategory));
+cl::opt<std::string> ICF("icf", cl::desc("fold functions with identical code"),
+                         cl::ValueOptional, cl::cat(BoltOptCategory));
 } // namespace opts
 
 namespace {
@@ -175,6 +171,16 @@ void BinaryContext::logBOLTErrorsAndQuitOnFatal(Error E) {
   });
 }
 
+static BinaryContext::ICFLevel parseICFLevel() {
+  if (!opts::ICF.getNumOccurrences())
+    return BinaryContext::ICFLevel::None;
+  std::string Str = StringRef(opts::ICF).lower();
+  return StringSwitch<BinaryContext::ICFLevel>(Str)
+      .Case("all", BinaryContext::ICFLevel::All)
+      .Case("safe", BinaryContext::ICFLevel::Safe)
+      .Default(BinaryContext::ICFLevel::All);
+}
+
 BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
                              std::unique_ptr<DWARFContext> DwCtx,
                              std::unique_ptr<Triple> TheTriple,
@@ -199,6 +205,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
       Logger(Logger), InitialDynoStats(isAArch64()) {
   RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
   PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
+  ICFLevelVar = parseICFLevel();
 }
 
 BinaryContext::~BinaryContext() {
@@ -2017,7 +2024,7 @@ static bool skipInstruction(const MCInst &Inst, const BinaryContext &BC) {
           BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst));
 }
 void BinaryContext::processInstructionForFuncReferences(const MCInst &Inst) {
-  if (!opts::SafeICF || skipInstruction(Inst, *this))
+  if (ICFLevelVar != ICFLevel::Safe || skipInstruction(Inst, *this))
     return;
   for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
     if (Op.isExpr()) {
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index f09482dc08c212..6e00ccda338556 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -550,7 +550,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
 
     LLVM_DEBUG(SinglePass.stopTimer());
   };
-  if (IsSafeICF) {
+  if (BC.getICFLevel() == BinaryContext::ICFLevel::Safe) {
     if (Error Err = createFoldSkipList(BC)) {
       return Err;
     }
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index df612c0a8f2e51..e5fb1f5b257850 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -54,8 +54,6 @@ extern cl::opt<bool> PrintDynoStats;
 extern cl::opt<bool> DumpDotAll;
 extern cl::opt<std::string> AsmDump;
 extern cl::opt<bolt::PLTCall::OptType> PLT;
-extern cl::opt<bool> ICF;
-extern cl::opt<bool> SafeICF;
 
 static cl::opt<bool>
 DynoStatsAll("dyno-stats-all",
@@ -396,9 +394,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
     Manager.registerPass(std::make_unique<StripRepRet>(NeverPrint),
                          opts::StripRepRet);
 
-  Manager.registerPass(
-      std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
-      opts::ICF || opts::SafeICF);
+  Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
+                       BC.getICFLevel() != BinaryContext::ICFLevel::None);
 
   Manager.registerPass(
       std::make_unique<SpecializeMemcpy1>(NeverPrint, opts::SpecializeMemcpy1),
@@ -422,9 +419,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
 
   Manager.registerPass(std::make_unique<Inliner>(PrintInline));
 
-  Manager.registerPass(
-      std::make_unique<IdenticalCodeFolding>(PrintICF, opts::SafeICF),
-      opts::ICF || opts::SafeICF);
+  Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
+                       BC.getICFLevel() != BinaryContext::ICFLevel::None);
 
   Manager.registerPass(std::make_unique<PLTCall>(PrintPLT));
 
diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp
index 1a8eb5cd2ce39d..5d3021ea845e7e 100644
--- a/bolt/lib/Rewrite/BoltDiff.cpp
+++ b/bolt/lib/Rewrite/BoltDiff.cpp
@@ -698,8 +698,8 @@ void RewriteInstance::compare(RewriteInstance &RI2) {
   }
 
   // Pre-pass ICF
-  if (opts::ICF) {
-    IdenticalCodeFolding ICF(opts::NeverPrint, opts::SafeICF);
+  if (BC->getICFLevel() != BinaryContext::ICFLevel::None) {
+    IdenticalCodeFolding ICF(opts::NeverPrint);
     outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
     BC->logBOLTErrorsAndQuitOnFatal(ICF.runOnFunctions(*BC));
     outs() << "BOLT-DIFF: Starting ICF pass for binary 2";
diff --git a/bolt/test/X86/icf-safe-icp.test b/bolt/test/X86/icf-safe-icp.test
index efeaf469eedeea..7b46c6297a40ec 100644
--- a/bolt/test/X86/icf-safe-icp.test
+++ b/bolt/test/X86/icf-safe-icp.test
@@ -6,7 +6,7 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFICPTest.s  -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
 # RUN: llvm-bolt --no-threads %t.exe --icf      -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test1-no-cfg.test b/bolt/test/X86/icf-safe-test1-no-cfg.test
index 37c5debe8c814b..5efbff9b3f3f76 100644
--- a/bolt/test/X86/icf-safe-test1-no-cfg.test
+++ b/bolt/test/X86/icf-safe-test1-no-cfg.test
@@ -4,8 +4,8 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
-# RUN: llvm-bolt --no-threads %t.exe --icf      -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf --skip-funcs=_Z7helper1PFiiiEii,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=all      -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf --skip-funcs=_Z7helper1PFiiiEii,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test1-no-relocs.test b/bolt/test/X86/icf-safe-test1-no-relocs.test
index 3b5d7400d85115..c80d35a5f576ff 100644
--- a/bolt/test/X86/icf-safe-test1-no-relocs.test
+++ b/bolt/test/X86/icf-safe-test1-no-relocs.test
@@ -1,11 +1,11 @@
-## Checks that BOLT handles correctly with no relocations with --safe-icf option.
+## Checks that BOLT handles correctly with no relocations with --icf=safe option.
 
 # REQUIRES: system-linux
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFTest1.s -o %t1.o
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe
 # RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: not llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: not llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
index 2b41633d07bf93..96986dec1e1922 100644
--- a/bolt/test/X86/icf-safe-test1.test
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -5,7 +5,7 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
 # RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO0.test b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
index 904d792e225b9c..9464dc05782ee5 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO0.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
@@ -5,7 +5,7 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
 # RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO3.test b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
index 288faf252ad745..935a5ddb189ef7 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO3.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
@@ -5,7 +5,7 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
 # RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO0.test b/bolt/test/X86/icf-safe-test3LocalVarO0.test
index 56a329b2f7a598..9fb783539da39f 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO0.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO0.test
@@ -5,7 +5,7 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
 # RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO3.test b/bolt/test/X86/icf-safe-test3LocalVarO3.test
index d0782e46b17e06..55fa11ada15490 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO3.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO3.test
@@ -5,7 +5,7 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICF.s    -o %t2.o
 # RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
 # RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
-# RUN: llvm-bolt --no-threads %t.exe --safe-icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
 
 ## Check that BOLT successfully folded a function with jump table:
 # ICFCHECK:      ICF iteration 1

>From d794fb02ad8312892a4d935a77ac03e4cdf98522 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Fri, 15 Nov 2024 14:29:54 -0800
Subject: [PATCH 3/6] addressed comments

---
 bolt/include/bolt/Core/BinaryContext.h         |  9 ++++-----
 bolt/include/bolt/Core/BinaryFunction.h        |  2 +-
 .../include/bolt/Passes/IdenticalCodeFolding.h |  5 +++--
 bolt/lib/Core/BinaryContext.cpp                | 18 +++++++++---------
 bolt/lib/Passes/IdenticalCodeFolding.cpp       |  8 ++++----
 5 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 3e58ec502acd09..318535b2fac239 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -278,9 +278,8 @@ class BinaryContext {
 public:
   enum class ICFLevel {
     None,
-    Safe, // Safe ICF for all sections.
-    All,  // Aggressive ICF for code, but safe ICF for data, similar to MSVC's
-          // behavior.
+    Safe,
+    All,
   };
   static Expected<std::unique_ptr<BinaryContext>>
   createBinaryContext(Triple TheTriple, StringRef InputFileName,
@@ -673,7 +672,7 @@ class BinaryContext {
   std::unique_ptr<MCAsmBackend> MAB;
 
   /// ICF level to use for this binary.
-  ICFLevel ICFLevelVar{ICFLevel::None};
+  ICFLevel CurrICFLevel{ICFLevel::None};
 
   /// Allows BOLT to print to log whenever it is necessary (with or without
   /// const references)
@@ -1494,7 +1493,7 @@ class BinaryContext {
     return *IOAddressMap;
   }
 
-  ICFLevel getICFLevel() const { return ICFLevelVar; }
+  ICFLevel getICFLevel() const { return CurrICFLevel; }
 
   raw_ostream &outs() const { return Logger.Out; }
 
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 5e1ddcc6bff9a2..f05ea40cfb1334 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -824,7 +824,7 @@ class BinaryFunction {
   bool isSafeToICF() const { return IsSafeToICF; }
 
   /// Sets the function is not safe to fold.
-  void setUnsetToICF() { IsSafeToICF = false; }
+  void setUnsafeICF() { IsSafeToICF = false; }
 
   /// Returns the raw binary encoding of this function.
   ErrorOr<ArrayRef<uint8_t>> getData() const;
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index 41fb17408d5876..eb803031bf0f0e 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -38,8 +38,9 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
   Error runOnFunctions(BinaryContext &BC) override;
 
 private:
-  /// Create a skip list of functions that should not be folded.
-  Error createFoldSkipList(BinaryContext &BC);
+  /// Analyses .text section and relocations and marks functions that are not
+  /// safe to fold.
+  Error markFunctionsUnsafeToFold(BinaryContext &BC);
   /// Processes relocations in the .data section to identify function
   /// references.
   void processDataRelocations(BinaryContext &BC,
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 6cf1562735eb45..960849371660b2 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -205,7 +205,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
       Logger(Logger), InitialDynoStats(isAArch64()) {
   RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
   PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
-  ICFLevelVar = parseICFLevel();
+  CurrICFLevel = parseICFLevel();
 }
 
 BinaryContext::~BinaryContext() {
@@ -2024,16 +2024,16 @@ static bool skipInstruction(const MCInst &Inst, const BinaryContext &BC) {
           BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst));
 }
 void BinaryContext::processInstructionForFuncReferences(const MCInst &Inst) {
-  if (ICFLevelVar != ICFLevel::Safe || skipInstruction(Inst, *this))
+  if (CurrICFLevel != ICFLevel::Safe || skipInstruction(Inst, *this))
     return;
   for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
-    if (Op.isExpr()) {
-      const MCExpr &Expr = *Op.getExpr();
-      if (Expr.getKind() == MCExpr::SymbolRef) {
-        const MCSymbol &Symbol = cast<MCSymbolRefExpr>(Expr).getSymbol();
-        if (BinaryFunction *BF = getFunctionForSymbol(&Symbol))
-          BF->setUnsetToICF();
-      }
+    if (!Op.isExpr())
+      continue;
+    const MCExpr &Expr = *Op.getExpr();
+    if (Expr.getKind() == MCExpr::SymbolRef) {
+      const MCSymbol &Symbol = cast<MCSymbolRefExpr>(Expr).getSymbol();
+      if (BinaryFunction *BF = getFunctionForSymbol(&Symbol))
+        BF->setUnsafeICF();
     }
   }
 }
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 6e00ccda338556..8fb22897a44f62 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -355,16 +355,16 @@ void IdenticalCodeFolding::processDataRelocations(
     const uint64_t SymbolAddress = cantFail(Symbol.getAddress());
     const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(OwningObj);
     if (!ELFObj)
-      assert(false && "Only ELFObjectFileBase is supported");
+      llvm_unreachable("Only ELFObjectFileBase is supported");
     const int64_t Addend = BinaryContext::getRelocationAddend(ELFObj, Rel);
     BinaryFunction *BF = BC.getBinaryFunctionAtAddress(SymbolAddress + Addend);
     if (!BF)
       continue;
-    BF->setUnsetToICF();
+    BF->setUnsafeICF();
   }
 }
 
-Error IdenticalCodeFolding::createFoldSkipList(BinaryContext &BC) {
+Error IdenticalCodeFolding::markFunctionsUnsafeToFold(BinaryContext &BC) {
   Error ErrorStatus = Error::success();
   ErrorOr<BinarySection &> SecRelData = BC.getUniqueSectionByName(".rela.data");
   if (!BC.HasRelocations)
@@ -551,7 +551,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
     LLVM_DEBUG(SinglePass.stopTimer());
   };
   if (BC.getICFLevel() == BinaryContext::ICFLevel::Safe) {
-    if (Error Err = createFoldSkipList(BC)) {
+    if (Error Err = markFunctionsUnsafeToFold(BC)) {
       return Err;
     }
   }

>From 6e853ca31324fdc32a172fb9c48d550ceab3547c Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Fri, 15 Nov 2024 16:41:00 -0800
Subject: [PATCH 4/6] Added four tests for pic/no-pic mode. Tests for global
 const function pointer

---
 .../Inputs/helperSafeICFGlobalConstPtrNoPic.s |  83 ++++
 ...lperSafeICFGlobalConstPtrNoPicExtFuncRef.s |  84 ++++
 .../Inputs/helperSafeICFGlobalConstPtrPic.s   |  84 ++++
 ...helperSafeICFGlobalConstPtrPicExtFuncRef.s |  84 ++++
 .../Inputs/mainSafeICFGlobalConstPtrNoPic.s   | 339 ++++++++++++++++
 ...mainSafeICFGlobalConstPtrNoPicExtFuncRef.s | 343 +++++++++++++++++
 .../X86/Inputs/mainSafeICFGlobalConstPtrPic.s | 350 +++++++++++++++++
 .../mainSafeICFGlobalConstPtrPicExtFuncRef.s  | 364 ++++++++++++++++++
 .../icf-safe-test2GlobalConstPtrNoPic.test    |  24 ++
 ...fe-test2GlobalConstPtrNoPicExtFuncRef.test |  24 ++
 .../X86/icf-safe-test2GlobalConstPtrPic.test  |  24 ++
 ...safe-test2GlobalConstPtrPicExtFuncRef.test |  24 ++
 12 files changed, 1827 insertions(+)
 create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s
 create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s
 create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s
 create mode 100644 bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s
 create mode 100644 bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s
 create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
 create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
 create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
 create mode 100644 bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test

diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s
new file mode 100644
index 00000000000000..8697f77862e66f
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPic.s
@@ -0,0 +1,83 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+#   return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+#   return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+	.text
+	.file	"helper.cpp"
+	.globl	_Z12barAddHdlperii              # -- Begin function _Z12barAddHdlperii
+	.p2align	4, 0x90
+	.type	_Z12barAddHdlperii, at function
+_Z12barAddHdlperii:                     # @_Z12barAddHdlperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z19fooGlobalFuncHelperii       # -- Begin function _Z19fooGlobalFuncHelperii
+	.p2align	4, 0x90
+	.type	_Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii:              # @_Z19fooGlobalFuncHelperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$16, %rsp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %edi
+	movl	-8(%rbp), %esi
+	callq	_Z12barAddHdlperii
+	addl	$5, %eax
+	addq	$16, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+	.cfi_endproc
+                                        # -- End function
+	.type	FooVar, at object                  # @FooVar
+	.data
+	.globl	FooVar
+	.p2align	2, 0x0
+FooVar:
+	.long	1                               # 0x1
+	.size	FooVar, 4
+
+	.type	BarVar, at object                  # @BarVar
+	.globl	BarVar
+	.p2align	2, 0x0
+BarVar:
+	.long	2                               # 0x2
+	.size	BarVar, 4
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s
new file mode 100644
index 00000000000000..489400c3ccaa63
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s
@@ -0,0 +1,84 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+#   return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+#   return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+
+	.text
+	.file	"helper.cpp"
+	.globl	_Z12barAddHdlperii              # -- Begin function _Z12barAddHdlperii
+	.p2align	4, 0x90
+	.type	_Z12barAddHdlperii, at function
+_Z12barAddHdlperii:                     # @_Z12barAddHdlperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z19fooGlobalFuncHelperii       # -- Begin function _Z19fooGlobalFuncHelperii
+	.p2align	4, 0x90
+	.type	_Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii:              # @_Z19fooGlobalFuncHelperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$16, %rsp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %edi
+	movl	-8(%rbp), %esi
+	callq	_Z12barAddHdlperii
+	addl	$5, %eax
+	addq	$16, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+	.cfi_endproc
+                                        # -- End function
+	.type	FooVar, at object                  # @FooVar
+	.data
+	.globl	FooVar
+	.p2align	2, 0x0
+FooVar:
+	.long	1                               # 0x1
+	.size	FooVar, 4
+
+	.type	BarVar, at object                  # @BarVar
+	.globl	BarVar
+	.p2align	2, 0x0
+BarVar:
+	.long	2                               # 0x2
+	.size	BarVar, 4
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s
new file mode 100644
index 00000000000000..489400c3ccaa63
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPic.s
@@ -0,0 +1,84 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+#   return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+#   return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+
+	.text
+	.file	"helper.cpp"
+	.globl	_Z12barAddHdlperii              # -- Begin function _Z12barAddHdlperii
+	.p2align	4, 0x90
+	.type	_Z12barAddHdlperii, at function
+_Z12barAddHdlperii:                     # @_Z12barAddHdlperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z19fooGlobalFuncHelperii       # -- Begin function _Z19fooGlobalFuncHelperii
+	.p2align	4, 0x90
+	.type	_Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii:              # @_Z19fooGlobalFuncHelperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$16, %rsp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %edi
+	movl	-8(%rbp), %esi
+	callq	_Z12barAddHdlperii
+	addl	$5, %eax
+	addq	$16, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+	.cfi_endproc
+                                        # -- End function
+	.type	FooVar, at object                  # @FooVar
+	.data
+	.globl	FooVar
+	.p2align	2, 0x0
+FooVar:
+	.long	1                               # 0x1
+	.size	FooVar, 4
+
+	.type	BarVar, at object                  # @BarVar
+	.globl	BarVar
+	.p2align	2, 0x0
+BarVar:
+	.long	2                               # 0x2
+	.size	BarVar, 4
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s
new file mode 100644
index 00000000000000..489400c3ccaa63
--- /dev/null
+++ b/bolt/test/X86/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s
@@ -0,0 +1,84 @@
+# clang++ helper.cpp -c -o
+# #define MY_CONST const
+# int FooVar = 1;
+# int BarVar = 2;
+# [[clang::noinline]]
+# MY_CONST int barAddHdlper(int a, int b) {
+#   return a + b;
+# }
+#
+# MY_CONST int (*const funcGlobalBarMulExt)(int, int) = barAddHdlper;
+# MY_CONST int fooGlobalFuncHelper(int a, int b) {
+#   return 5 + funcGlobalBarMulExt(a, b);
+# }
+
+
+	.text
+	.file	"helper.cpp"
+	.globl	_Z12barAddHdlperii              # -- Begin function _Z12barAddHdlperii
+	.p2align	4, 0x90
+	.type	_Z12barAddHdlperii, at function
+_Z12barAddHdlperii:                     # @_Z12barAddHdlperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z12barAddHdlperii, .Lfunc_end0-_Z12barAddHdlperii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z19fooGlobalFuncHelperii       # -- Begin function _Z19fooGlobalFuncHelperii
+	.p2align	4, 0x90
+	.type	_Z19fooGlobalFuncHelperii, at function
+_Z19fooGlobalFuncHelperii:              # @_Z19fooGlobalFuncHelperii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$16, %rsp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %edi
+	movl	-8(%rbp), %esi
+	callq	_Z12barAddHdlperii
+	addl	$5, %eax
+	addq	$16, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z19fooGlobalFuncHelperii, .Lfunc_end1-_Z19fooGlobalFuncHelperii
+	.cfi_endproc
+                                        # -- End function
+	.type	FooVar, at object                  # @FooVar
+	.data
+	.globl	FooVar
+	.p2align	2, 0x0
+FooVar:
+	.long	1                               # 0x1
+	.size	FooVar, 4
+
+	.type	BarVar, at object                  # @BarVar
+	.globl	BarVar
+	.p2align	2, 0x0
+BarVar:
+	.long	2                               # 0x2
+	.size	BarVar, 4
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z12barAddHdlperii
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s
new file mode 100644
index 00000000000000..c113f1b45ac1a2
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPic.s
@@ -0,0 +1,339 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# MY_CONST static int (*MY_CONST funcGlobalBarAdd)(int, int) = barAdd;
+# MY_CONST int (*MY_CONST funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   MY_PRINTF("val: %d", temp);
+#   return temp;
+# }
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFKiiiEii             # -- Begin function _Z7helper1PFKiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii:                    # @_Z7helper1PFKiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	movabsq	$_Z6barAddii, %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFKiiiES1_ii          # -- Begin function _Z7helper2PFKiiiES1_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii:                 # @_Z7helper2PFKiiiES1_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	movl	FooVar, %esi
+	movl	BarVar, %edx
+	movabsq	$_Z6barAddii, %rdi
+	callq	_Z7helper1PFKiiiEii
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edx
+	movl	BarVar, %ecx
+	movabsq	$_Z6fooMulii, %rdi
+	movabsq	$_Z6barMulii, %rsi
+	callq	_Z7helper2PFKiiiES1_ii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edi
+	movl	BarVar, %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edi
+	movl	BarVar, %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-28(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -24(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edi
+	movl	BarVar, %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-24(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -20(%rbp)
+	movl	-20(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFKiiiEii
+	.addrsig_sym _Z7helper2PFKiiiES1_ii
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s
new file mode 100644
index 00000000000000..40f30cbbc30550
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s
@@ -0,0 +1,343 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# extern MY_CONST int barAddHdlper(int a, int b);
+# extern MY_CONST int fooGlobalFuncHelper(int a, int b);
+# MY_CONST static int (*const funcGlobalBarAdd)(int, int) = barAddHdlper;
+# MY_CONST int (* const funcGlobalBarMul)(int, int) = fooGlobalFuncHelper;
+# int main(int argc, char **argv) {
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   MY_PRINTF("val: %d", temp);
+#   return temp;
+# }
+
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFKiiiEii             # -- Begin function _Z7helper1PFKiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii:                    # @_Z7helper1PFKiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	movabsq	$_Z6barAddii, %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFKiiiES1_ii          # -- Begin function _Z7helper2PFKiiiES1_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii:                 # @_Z7helper2PFKiiiES1_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	movl	FooVar, %esi
+	movl	BarVar, %edx
+	movabsq	$_Z12barAddHdlperii, %rdi
+	callq	_Z7helper1PFKiiiEii
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edx
+	movl	BarVar, %ecx
+	movabsq	$_Z6fooMulii, %rdi
+	movabsq	$_Z19fooGlobalFuncHelperii, %rsi
+	callq	_Z7helper2PFKiiiES1_ii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edi
+	movl	BarVar, %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edi
+	movl	BarVar, %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-28(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -24(%rbp)                 # 4-byte Spill
+	movl	FooVar, %edi
+	movl	BarVar, %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-24(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -20(%rbp)
+	movl	-20(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFKiiiEii
+	.addrsig_sym _Z7helper2PFKiiiES1_ii
+	.addrsig_sym _Z12barAddHdlperii
+	.addrsig_sym _Z19fooGlobalFuncHelperii
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s
new file mode 100644
index 00000000000000..16976abc7d081c
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPic.s
@@ -0,0 +1,350 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# MY_CONST static int (*MY_CONST funcGlobalBarAdd)(int, int) = barAdd;
+# MY_CONST int (*MY_CONST funcGlobalBarMul)(int, int) = barMul;
+# int main(int argc, char **argv) {
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   MY_PRINTF("val: %d", temp);
+#   return temp;
+# }
+
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFKiiiEii             # -- Begin function _Z7helper1PFKiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii:                    # @_Z7helper1PFKiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	leaq	_Z6barAddii(%rip), %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFKiiiES1_ii          # -- Begin function _Z7helper2PFKiiiES1_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii:                 # @_Z7helper2PFKiiiES1_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	leaq	_Z6barAddii(%rip), %rdi
+	callq	_Z7helper1PFKiiiEii
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	leaq	_Z6barMulii(%rip), %rsi
+	callq	_Z7helper2PFKiiiES1_ii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-28(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -24(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-24(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -20(%rbp)
+	movl	-20(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6barMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFKiiiEii
+	.addrsig_sym _Z7helper2PFKiiiES1_ii
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s
new file mode 100644
index 00000000000000..a7da985b93bc38
--- /dev/null
+++ b/bolt/test/X86/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s
@@ -0,0 +1,364 @@
+# clang++ main.cpp -c -o
+# #define MY_CONST const
+# extern int FooVar;
+# extern int BarVar;
+# [[clang::noinline]]
+# MY_CONST int fooSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barSub(int a, int b) {
+#   return a - b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barMul(int a, int b) {
+#   return a * b;
+# }
+# [[clang::noinline]]
+# MY_CONST int fooAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int barAdd(int a, int b) {
+#   return a + b;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper1(MY_CONST int (*func)(int, int), int a, int b) {
+#   if (func == barAdd)
+#     return 1;
+#   return func(a, b) - 4;
+# }
+# [[clang::noinline]]
+# MY_CONST int helper2(MY_CONST int (*func)(int, int), MY_CONST int (*func2)(int, int), int a, int b) {
+#   if (func == func2)
+#     return 2;
+#   return func(a, b) + func2(a, b);
+# }
+# extern MY_CONST int barAddHdlper(int a, int b);
+# extern MY_CONST int fooGlobalFuncHelper(int a, int b);
+# MY_CONST static int (*const funcGlobalBarAdd)(int, int) = barAddHdlper;
+# MY_CONST int (* const funcGlobalBarMul)(int, int) = fooGlobalFuncHelper;
+# int main(int argc, char **argv) {
+#   int temp = helper1(funcGlobalBarAdd, FooVar, BarVar) +
+#              helper2(fooMul, funcGlobalBarMul, FooVar, BarVar) + fooSub(FooVar, BarVar) +
+#              barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+#   MY_PRINTF("val: %d", temp);
+#   return temp;
+# }
+
+	.text
+	.file	"main.cpp"
+	.globl	_Z6fooSubii                     # -- Begin function _Z6fooSubii
+	.p2align	4, 0x90
+	.type	_Z6fooSubii, at function
+_Z6fooSubii:                            # @_Z6fooSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end0:
+	.size	_Z6fooSubii, .Lfunc_end0-_Z6fooSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barSubii                     # -- Begin function _Z6barSubii
+	.p2align	4, 0x90
+	.type	_Z6barSubii, at function
+_Z6barSubii:                            # @_Z6barSubii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	subl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_Z6barSubii, .Lfunc_end1-_Z6barSubii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooMulii                     # -- Begin function _Z6fooMulii
+	.p2align	4, 0x90
+	.type	_Z6fooMulii, at function
+_Z6fooMulii:                            # @_Z6fooMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end2:
+	.size	_Z6fooMulii, .Lfunc_end2-_Z6fooMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barMulii                     # -- Begin function _Z6barMulii
+	.p2align	4, 0x90
+	.type	_Z6barMulii, at function
+_Z6barMulii:                            # @_Z6barMulii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	imull	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end3:
+	.size	_Z6barMulii, .Lfunc_end3-_Z6barMulii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6fooAddii                     # -- Begin function _Z6fooAddii
+	.p2align	4, 0x90
+	.type	_Z6fooAddii, at function
+_Z6fooAddii:                            # @_Z6fooAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end4:
+	.size	_Z6fooAddii, .Lfunc_end4-_Z6fooAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z6barAddii                     # -- Begin function _Z6barAddii
+	.p2align	4, 0x90
+	.type	_Z6barAddii, at function
+_Z6barAddii:                            # @_Z6barAddii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movl	%esi, -8(%rbp)
+	movl	-4(%rbp), %eax
+	addl	-8(%rbp), %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end5:
+	.size	_Z6barAddii, .Lfunc_end5-_Z6barAddii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper1PFKiiiEii             # -- Begin function _Z7helper1PFKiiiEii
+	.p2align	4, 0x90
+	.type	_Z7helper1PFKiiiEii, at function
+_Z7helper1PFKiiiEii:                    # @_Z7helper1PFKiiiEii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$32, %rsp
+	movq	%rdi, -16(%rbp)
+	movl	%esi, -20(%rbp)
+	movl	%edx, -24(%rbp)
+	leaq	_Z6barAddii(%rip), %rax
+	cmpq	%rax, -16(%rbp)
+	jne	.LBB6_2
+# %bb.1:
+	movl	$1, -4(%rbp)
+	jmp	.LBB6_3
+.LBB6_2:
+	movq	-16(%rbp), %rax
+	movl	-20(%rbp), %edi
+	movl	-24(%rbp), %esi
+	callq	*%rax
+	subl	$4, %eax
+	movl	%eax, -4(%rbp)
+.LBB6_3:
+	movl	-4(%rbp), %eax
+	addq	$32, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end6:
+	.size	_Z7helper1PFKiiiEii, .Lfunc_end6-_Z7helper1PFKiiiEii
+	.cfi_endproc
+                                        # -- End function
+	.globl	_Z7helper2PFKiiiES1_ii          # -- Begin function _Z7helper2PFKiiiES1_ii
+	.p2align	4, 0x90
+	.type	_Z7helper2PFKiiiES1_ii, at function
+_Z7helper2PFKiiiES1_ii:                 # @_Z7helper2PFKiiiES1_ii
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movq	%rdi, -16(%rbp)
+	movq	%rsi, -24(%rbp)
+	movl	%edx, -28(%rbp)
+	movl	%ecx, -32(%rbp)
+	movq	-16(%rbp), %rax
+	cmpq	-24(%rbp), %rax
+	jne	.LBB7_2
+# %bb.1:
+	movl	$2, -4(%rbp)
+	jmp	.LBB7_3
+.LBB7_2:
+	movq	-16(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	-24(%rbp), %rax
+	movl	-28(%rbp), %edi
+	movl	-32(%rbp), %esi
+	callq	*%rax
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -4(%rbp)
+.LBB7_3:
+	movl	-4(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end7:
+	.size	_Z7helper2PFKiiiES1_ii, .Lfunc_end7-_Z7helper2PFKiiiES1_ii
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	movq	_Z12barAddHdlperii at GOTPCREL(%rip), %rdi
+	callq	_Z7helper1PFKiiiEii
+	movl	%eax, -36(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edx
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %ecx
+	leaq	_Z6fooMulii(%rip), %rdi
+	movq	_Z19fooGlobalFuncHelperii at GOTPCREL(%rip), %rsi
+	callq	_Z7helper2PFKiiiES1_ii
+	movl	%eax, %ecx
+	movl	-36(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -32(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooSubii
+	movl	%eax, %ecx
+	movl	-32(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -28(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6barSubii
+	movl	%eax, %ecx
+	movl	-28(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -24(%rbp)                 # 4-byte Spill
+	movq	FooVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %edi
+	movq	BarVar at GOTPCREL(%rip), %rax
+	movl	(%rax), %esi
+	callq	_Z6fooAddii
+	movl	%eax, %ecx
+	movl	-24(%rbp), %eax                 # 4-byte Reload
+	addl	%ecx, %eax
+	movl	%eax, -20(%rbp)
+	movl	-20(%rbp), %esi
+	leaq	.L.str(%rip), %rdi
+	movb	$0, %al
+	callq	printf at PLT
+	movl	-20(%rbp), %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end8:
+	.size	main, .Lfunc_end8-main
+	.cfi_endproc
+                                        # -- End function
+	.type	.L.str, at object                  # @.str
+	.section	.rodata.str1.1,"aMS", at progbits,1
+.L.str:
+	.asciz	"val: %d\n"
+	.size	.L.str, 9
+
+	.ident	"clang version 20.0.0git"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z6fooSubii
+	.addrsig_sym _Z6barSubii
+	.addrsig_sym _Z6fooMulii
+	.addrsig_sym _Z6fooAddii
+	.addrsig_sym _Z6barAddii
+	.addrsig_sym _Z7helper1PFKiiiEii
+	.addrsig_sym _Z7helper2PFKiiiES1_ii
+	.addrsig_sym _Z12barAddHdlperii
+	.addrsig_sym _Z19fooGlobalFuncHelperii
+	.addrsig_sym printf
+	.addrsig_sym FooVar
+	.addrsig_sym BarVar
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
new file mode 100644
index 00000000000000..332c4296f45e5f
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer in -fno-pic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrNoPic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrNoPic.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q -no-pie
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
new file mode 100644
index 00000000000000..a46fdb86574c0b
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer in -fno-pic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrNoPicExtFuncRef.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrNoPicExtFuncRef.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q -no-pie
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
new file mode 100644
index 00000000000000..ab84fdc619ae9b
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer in -fpic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrPic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrPic.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6barMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
new file mode 100644
index 00000000000000..f416f8f5f4f9c8
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
@@ -0,0 +1,24 @@
+## Check that BOLT handles correctly folding functions with --icf-safe that can be referenced.
+## This checks global const function pointer with external function reference in -fpic mode is handled correctly.
+
+# REQUIRES: system-linux
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/mainSafeICFGlobalConstPtrPicExtFuncRef.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %p/Inputs/helperSafeICFGlobalConstPtrPicExtFuncRef.s    -o %t2.o
+# RUN: %clang %cflags %t1.o %t2.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+## Check that BOLT successfully folded a function with jump table:
+# ICFCHECK:      ICF iteration 1
+# ICFCHECK-NEXT: folding _Z6barAddii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
+# ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
+# ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+
+# SAFEICFCHECK:      skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
+# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
+# SAFEICFCHECK-NEXT: ===---------

>From e36ec02bfeb440e56a9b8593f454a9e7f2fe7ed5 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Fri, 15 Nov 2024 18:17:01 -0800
Subject: [PATCH 5/6] switched to enum version of icf compile option

---
 bolt/lib/Core/BinaryContext.cpp     | 24 +++++++++++-------------
 bolt/test/X86/shared_object_entry.s |  2 +-
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 960849371660b2..aaa7a125e94147 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -76,8 +76,16 @@ cl::opt<std::string> CompDirOverride(
              "to *.dwo files."),
     cl::Hidden, cl::init(""), cl::cat(BoltCategory));
 
-cl::opt<std::string> ICF("icf", cl::desc("fold functions with identical code"),
-                         cl::ValueOptional, cl::cat(BoltOptCategory));
+cl::opt<bolt::BinaryContext::ICFLevel> ICF(
+    "icf", cl::desc("fold functions with identical code"),
+    cl::init(bolt::BinaryContext::ICFLevel::None),
+    cl::values(
+        clEnumValN(bolt::BinaryContext::ICFLevel::All, "all", "enable ICF"),
+        clEnumValN(bolt::BinaryContext::ICFLevel::All, "", "enable ICF"),
+        clEnumValN(bolt::BinaryContext::ICFLevel::None, "none", "disable ICF"),
+        clEnumValN(bolt::BinaryContext::ICFLevel::Safe, "safe",
+                   "enable safe ICF")),
+    cl::ZeroOrMore, cl::ValueOptional, cl::cat(BoltOptCategory));
 } // namespace opts
 
 namespace {
@@ -171,16 +179,6 @@ void BinaryContext::logBOLTErrorsAndQuitOnFatal(Error E) {
   });
 }
 
-static BinaryContext::ICFLevel parseICFLevel() {
-  if (!opts::ICF.getNumOccurrences())
-    return BinaryContext::ICFLevel::None;
-  std::string Str = StringRef(opts::ICF).lower();
-  return StringSwitch<BinaryContext::ICFLevel>(Str)
-      .Case("all", BinaryContext::ICFLevel::All)
-      .Case("safe", BinaryContext::ICFLevel::Safe)
-      .Default(BinaryContext::ICFLevel::All);
-}
-
 BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
                              std::unique_ptr<DWARFContext> DwCtx,
                              std::unique_ptr<Triple> TheTriple,
@@ -205,7 +203,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
       Logger(Logger), InitialDynoStats(isAArch64()) {
   RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
   PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
-  CurrICFLevel = parseICFLevel();
+  CurrICFLevel = opts::ICF;
 }
 
 BinaryContext::~BinaryContext() {
diff --git a/bolt/test/X86/shared_object_entry.s b/bolt/test/X86/shared_object_entry.s
index 87a3c0655533d1..2caab5afdb7805 100644
--- a/bolt/test/X86/shared_object_entry.s
+++ b/bolt/test/X86/shared_object_entry.s
@@ -2,7 +2,7 @@
 # RUN: ld.lld %t.o -o %t.so --shared --entry=func1.cold.1 --emit-relocs
 # RUN: llvm-bolt -relocs %t.so -o %t -reorder-functions=hfsort+ \
 # RUN:    -split-functions -reorder-blocks=ext-tsp -split-all-cold \
-# RUN:    -dyno-stats -icf=1 -use-gnu-stack
+# RUN:    -dyno-stats -icf -use-gnu-stack
 
 ## Check that an entry point is a cold symbol
 # RUN: llvm-readelf -h %t.so > %t.log

>From b83c3350ec08623350c1639347cb9f8e71850fba Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Tue, 19 Nov 2024 16:21:32 -0800
Subject: [PATCH 6/6] simplified debug print, removed shared func

---
 .../bolt/Passes/IdenticalCodeFolding.h        |  2 ++
 bolt/lib/Passes/IdenticalCodeFolding.cpp      | 29 +++++--------------
 bolt/test/X86/icf-safe-test1-no-cfg.test      |  4 +--
 bolt/test/X86/icf-safe-test1.test             |  4 +--
 .../icf-safe-test2GlobalConstPtrNoPic.test    |  4 +--
 ...fe-test2GlobalConstPtrNoPicExtFuncRef.test |  6 ++--
 .../X86/icf-safe-test2GlobalConstPtrPic.test  |  4 +--
 ...safe-test2GlobalConstPtrPicExtFuncRef.test |  6 ++--
 bolt/test/X86/icf-safe-test2GlobalVarO0.test  |  4 +--
 bolt/test/X86/icf-safe-test2GlobalVarO3.test  |  4 +--
 bolt/test/X86/icf-safe-test3LocalVarO0.test   |  4 +--
 bolt/test/X86/icf-safe-test3LocalVarO3.test   |  4 +--
 12 files changed, 32 insertions(+), 43 deletions(-)

diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index eb803031bf0f0e..9d8d3ece1a2edf 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -27,6 +27,8 @@ class IdenticalCodeFolding : public BinaryFunctionPass {
       return false;
     if (BF.hasSDTMarker())
       return false;
+    if (!BF.isSafeToICF())
+      return false;
     return BinaryFunctionPass::shouldOptimize(BF);
   }
 
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 8fb22897a44f62..2114bfe874bd18 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -393,22 +393,12 @@ Error IdenticalCodeFolding::markFunctionsUnsafeToFold(BinaryContext &BC) {
       "markUnsafe", /*ForceSequential*/ false, 2);
 
   LLVM_DEBUG({
-    std::vector<StringRef> Vect;
-    std::mutex PrintMutex;
-    ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
-      if (BF.isSafeToICF())
-        return;
-      std::lock_guard<std::mutex> Lock(PrintMutex);
-      Vect.push_back(BF.getOneName());
-    };
-    ParallelUtilities::PredicateTy SkipFunc =
-        [&](const BinaryFunction &BF) -> bool { return false; };
-    ParallelUtilities::runOnEachFunction(
-        BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
-        "markUnsafe", /*ForceSequential*/ false, 2);
-    llvm::sort(Vect);
-    for (const auto &FuncName : Vect)
-      dbgs() << "BOLT-DEBUG: skipping function " << FuncName << '\n';
+    for (auto &BFIter : BC.getBinaryFunctions()) {
+      if (BFIter.second.isSafeToICF())
+        continue;
+      dbgs() << "BOLT-DEBUG: skipping function " << BFIter.second.getOneName()
+             << '\n';
+    }
   });
   return ErrorStatus;
 }
@@ -422,9 +412,6 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
   std::atomic<uint64_t> NumFoldedLastIteration{0};
   CongruentBucketsMap CongruentBuckets;
 
-  auto SkipFuncShared = [&](const BinaryFunction &BF) {
-    return !shouldOptimize(BF) || !BF.isSafeToICF();
-  };
   // Hash all the functions
   auto hashFunctions = [&]() {
     NamedRegionTimer HashFunctionsTimer("hashing", "hashing", "ICF breakdown",
@@ -444,7 +431,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
     };
 
     ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
-      return SkipFuncShared(BF);
+      return !shouldOptimize(BF);
     };
 
     ParallelUtilities::runOnEachFunction(
@@ -460,7 +447,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
                                            "ICF breakdown", opts::TimeICF);
     for (auto &BFI : BC.getBinaryFunctions()) {
       BinaryFunction &BF = BFI.second;
-      if (SkipFuncShared(BF))
+      if (!shouldOptimize(BF))
         continue;
       CongruentBuckets[&BF].emplace(&BF);
     }
diff --git a/bolt/test/X86/icf-safe-test1-no-cfg.test b/bolt/test/X86/icf-safe-test1-no-cfg.test
index 5efbff9b3f3f76..8617dfa590d941 100644
--- a/bolt/test/X86/icf-safe-test1-no-cfg.test
+++ b/bolt/test/X86/icf-safe-test1-no-cfg.test
@@ -13,9 +13,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
index 96986dec1e1922..b60795d7fa3bee 100644
--- a/bolt/test/X86/icf-safe-test1.test
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -13,9 +13,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
index 332c4296f45e5f..83e7e798afcd8c 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
@@ -15,9 +15,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
index a46fdb86574c0b..bef5b71a25c4f6 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPicExtFuncRef.test
@@ -15,10 +15,10 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z12barAddHdlperii
-# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barAddii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
index ab84fdc619ae9b..d2c039769e8d23 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPic.test
@@ -15,9 +15,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z12barAddHdlperii into _Z6fooAddii
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
index f416f8f5f4f9c8..2a93af6d44220c 100644
--- a/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrPicExtFuncRef.test
@@ -15,10 +15,10 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z12barAddHdlperii
-# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barAddii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z12barAddHdlperii
+# SAFEICFCHECK-NEXT: skipping function _Z19fooGlobalFuncHelperii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO0.test b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
index 9464dc05782ee5..de5c1ebca81e17 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO0.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO0.test
@@ -13,9 +13,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test2GlobalVarO3.test b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
index 935a5ddb189ef7..06853bfe328904 100644
--- a/bolt/test/X86/icf-safe-test2GlobalVarO3.test
+++ b/bolt/test/X86/icf-safe-test2GlobalVarO3.test
@@ -13,9 +13,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO0.test b/bolt/test/X86/icf-safe-test3LocalVarO0.test
index 9fb783539da39f..0a88cca49e1bd4 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO0.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO0.test
@@ -13,9 +13,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------
diff --git a/bolt/test/X86/icf-safe-test3LocalVarO3.test b/bolt/test/X86/icf-safe-test3LocalVarO3.test
index 55fa11ada15490..d961980df15be6 100644
--- a/bolt/test/X86/icf-safe-test3LocalVarO3.test
+++ b/bolt/test/X86/icf-safe-test3LocalVarO3.test
@@ -13,9 +13,9 @@
 # ICFCHECK-NEXT: folding _Z6barMulii into _Z6fooMulii
 # ICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 
-# SAFEICFCHECK:      skipping function _Z6barAddii
+# SAFEICFCHECK:      skipping function _Z6fooMulii
 # SAFEICFCHECK-NEXT: skipping function _Z6barMulii
-# SAFEICFCHECK-NEXT: skipping function _Z6fooMulii
+# SAFEICFCHECK-NEXT: skipping function _Z6barAddii
 # SAFEICFCHECK-NEXT: ICF iteration 1
 # SAFEICFCHECK-NEXT: folding _Z6barSubii into _Z6fooSubii
 # SAFEICFCHECK-NEXT: ===---------



More information about the llvm-commits mailing list