[llvm] [BOLT] Enable relocation recovery for stripped ELF binaries (PR #221991)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 06:12:02 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-bolt

Author: Wenlong Mu (onroadmuwl)

<details>
<summary>Changes</summary>

Add opt-in relocation recovery for stripped AArch64 and x86-64 ELF binaries that lack the static relocation information normally required by BOLT's relocation-mode rewriting.

The `--recover-relocations` option enables relocation recovery when the required static relocation information is unavailable. The `--aggressive-relocation-recovery` option additionally recovers function pointers from general data sections and requires `--recover-relocations` to be enabled. These options are opt-in and do not affect BOLT's existing rewriting mode for binaries that retain symbol tables and static relocation information.

The implementation recovers code and data references required during binary rewriting and updates them after code layout changes. It also supports relocating jump tables together with optimized code and updating the corresponding references.

On AArch64, the implementation recovers ADRP/ADD address references and function pointers from supported data sections. When the complete target of an ADRP/ADD reference cannot be determined unambiguously, the original ADRP page is preserved while the ADD immediate remains unchanged.

For stripped AArch64 binaries, the patch also recovers additional functions that may not be discoverable from the remaining binary metadata, including the process entry point (`_start`), its optional glibc main-wrapper secondary entry, and Cortex-A53 erratum 843419 veneers.

Regression tests are added for both AArch64 and x86-64 to validate relocation recovery and the correctness of rewritten addresses.

The implementation has also been evaluated on SPEC CPU2017 and real-world applications including MySQL and MongoDB. The optimized stripped binaries achieve performance results close to those produced by BOLT on binaries retaining symbol tables and static relocation information.

---

Patch is 59.66 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/221991.diff


20 Files Affected:

- (modified) bolt/include/bolt/Core/BinaryContext.h (+6-1) 
- (added) bolt/include/bolt/Passes/RelocationRecovery.h (+33) 
- (modified) bolt/include/bolt/Rewrite/RewriteInstance.h (+5-1) 
- (modified) bolt/lib/Core/BinaryContext.cpp (+56-25) 
- (modified) bolt/lib/Core/BinaryFunction.cpp (+5-1) 
- (modified) bolt/lib/Passes/CMakeLists.txt (+1) 
- (added) bolt/lib/Passes/RelocationRecovery.cpp (+361) 
- (modified) bolt/lib/Rewrite/BinaryPassManager.cpp (+10) 
- (modified) bolt/lib/Rewrite/CMakeLists.txt (+1) 
- (modified) bolt/lib/Rewrite/RewriteInstance.cpp (+70-7) 
- (added) bolt/lib/Rewrite/StrippedBinary.cpp (+285) 
- (added) bolt/test/AArch64/recover-relocations-ambiguous.s (+33) 
- (added) bolt/test/AArch64/recover-relocations.s (+92) 
- (added) bolt/test/AArch64/stripped-entry.s (+52) 
- (added) bolt/test/AArch64/stripped-erratum-843419.s (+57) 
- (added) bolt/test/Inputs/check-recovered-addresses.py (+75) 
- (added) bolt/test/X86/recover-relocations-data.s (+45) 
- (added) bolt/test/X86/stripped-jump-table.s (+49) 
- (modified) llvm/utils/gn/secondary/bolt/lib/Passes/BUILD.gn (+1) 
- (modified) llvm/utils/gn/secondary/bolt/lib/Rewrite/BUILD.gn (+1) 


``````````diff
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 2ccccde342e7f..f50d25add3894 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -767,9 +767,14 @@ class BinaryContext {
   /// Indicates if the binary is Linux kernel.
   bool IsLinuxKernel{false};
 
-  /// Indicates if relocations are available for usage.
+  /// Indicates whether the relocation-mode rewriting pipeline is active.
   bool HasRelocations{false};
 
+  /// True when --recover-relocations is enabled. BOLT reconstructs code and
+  /// data references that are missing from the input static relocation records,
+  /// then uses relocation-mode rewriting to move the referenced functions.
+  bool RecoverRelocations{false};
+
   /// Indicates if the binary is stripped
   bool IsStripped{false};
 
diff --git a/bolt/include/bolt/Passes/RelocationRecovery.h b/bolt/include/bolt/Passes/RelocationRecovery.h
new file mode 100644
index 0000000000000..047acaffede08
--- /dev/null
+++ b/bolt/include/bolt/Passes/RelocationRecovery.h
@@ -0,0 +1,33 @@
+//===- bolt/Passes/RelocationRecovery.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_PASSES_RELOCATIONRECOVERY_H
+#define BOLT_PASSES_RELOCATIONRECOVERY_H
+
+#include "bolt/Passes/BinaryPasses.h"
+
+namespace llvm {
+namespace bolt {
+
+/// Reconstruct code and data address references that are missing from the
+/// input static relocation records. If the complete target of an AArch64
+/// ADRP/ADD reference cannot be determined unambiguously, rewrite the ADRP to
+/// reproduce its original absolute page and leave the ADD immediate unchanged.
+class RelocationRecovery : public BinaryFunctionPass {
+public:
+  explicit RelocationRecovery(const cl::opt<bool> &PrintPass)
+      : BinaryFunctionPass(PrintPass) {}
+
+  const char *getName() const override { return "recover-relocations"; }
+  Error runOnFunctions(BinaryContext &BC) override;
+};
+
+} // namespace bolt
+} // namespace llvm
+
+#endif // BOLT_PASSES_RELOCATIONRECOVERY_H
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 4d4ac70195bc2..4c1028b723f11 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -87,7 +87,7 @@ class RewriteInstance {
 private:
   /// Populate array of binary functions and other objects of interest
   /// from meta data in the file.
-  void discoverFileObjects();
+  Error discoverFileObjects();
 
   /// Check if the input binary has a space reserved for BOLT and use it for new
   /// section allocations if found.
@@ -175,6 +175,10 @@ class RewriteInstance {
   /// optimization.
   void disassembleFunctions();
 
+  /// Discover AArch64 process-entry code and linker-generated veneers in a
+  /// stripped input.
+  Error discoverStrippedFunctions();
+
   void buildFunctionsCFG();
 
   void postProcessFunctions();
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index c40851d9aaa98..296cd3b7ca7dd 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -635,6 +635,8 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
                                      const uint64_t NextJTAddress,
                                      JumpTable::AddressesType *EntriesAsAddress,
                                      bool *HasEntryInFragment) const {
+  const bool HasSymbolTable = !IsStripped;
+
   // Target address of __builtin_unreachable.
   const uint64_t UnreachableAddress = BF.getAddress() + BF.getSize();
 
@@ -701,7 +703,7 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
     LLVM_DEBUG(dbgs() << "  * Checking 0x" << Twine::utohexstr(EntryAddress)
                       << " -> ");
     // Check if there's a proper relocation against the jump table entry.
-    if (HasRelocations) {
+    if (HasRelocations && !RecoverRelocations) {
       if (Type == JumpTable::JTT_PIC &&
           !DataPCRelocations.count(EntryAddress)) {
         LLVM_DEBUG(
@@ -740,7 +742,16 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
 
     // Function or one of its fragments.
     const BinaryFunction *TargetBF = getBinaryFunctionContainingAddress(Value);
-    if (!TargetBF || !areRelatedFragments(TargetBF, &BF)) {
+    if (!TargetBF) {
+      LLVM_DEBUG(printEntryDiagnostics(dbgs(), TargetBF));
+      (void)printEntryDiagnostics;
+      break;
+    }
+
+    // areRelatedFragments() identifies split-function fragments by symbol name.
+    // A stripped binary has no names for this check, so skip it after verifying
+    // above that Value lies within a BinaryFunction registered in the context.
+    if (HasSymbolTable && !areRelatedFragments(TargetBF, &BF)) {
       LLVM_DEBUG(printEntryDiagnostics(dbgs(), TargetBF));
       (void)printEntryDiagnostics;
       break;
@@ -934,20 +945,28 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
     if (llvm::is_contained(JT->Parents, &Function))
       return JT->getFirstLabel();
 
-    // Prevent associating a jump table to a specific fragment twice.
-    auto isSibling = std::bind(&BinaryContext::areRelatedFragments, this,
-                               &Function, std::placeholders::_1);
-    assert(llvm::all_of(JT->Parents, isSibling) &&
-           "cannot reuse jump table of a different function");
-    (void)isSibling;
+    // Symbol names establish fragment relationships. Without a symbol table,
+    // separately discovered functions can refer to the same jump table but
+    // cannot be classified as fragments by name.
+    if (!IsStripped) {
+      auto IsSibling = std::bind(&BinaryContext::areRelatedFragments, this,
+                                 &Function, std::placeholders::_1);
+      assert(llvm::all_of(JT->Parents, IsSibling) &&
+             "cannot reuse jump table of a different function");
+      (void)IsSibling;
+    }
     if (opts::Verbosity > 2) {
-      this->outs() << "BOLT-INFO: multiple fragments access the same jump table"
-                   << ": " << *JT->Parents[0] << "; " << Function << '\n';
+      this->outs() << "BOLT-INFO: multiple "
+                   << (IsStripped ? "functions" : "fragments")
+                   << " access the same jump table: " << *JT->Parents[0] << "; "
+                   << Function << '\n';
       JT->print(this->outs());
     }
-    if (JT->Parents.size() == 1)
-      JT->Parents.front()->setHasIndirectTargetToSplitFragment(true);
-    Function.setHasIndirectTargetToSplitFragment(true);
+    if (!IsStripped) {
+      if (JT->Parents.size() == 1)
+        JT->Parents.front()->setHasIndirectTargetToSplitFragment(true);
+      Function.setHasIndirectTargetToSplitFragment(true);
+    }
     // Duplicate the entry for the parent function for easy access
     JT->Parents.push_back(&Function);
     Function.JumpTables.emplace(Address, JT);
@@ -1093,20 +1112,32 @@ bool BinaryContext::hasValidCodePadding(const BinaryFunction &BF) {
 
   auto isNoop = std::bind(&MCPlusBuilder::isNoop, MIB.get(), _1);
 
-  // Some functions have a jump to the next function or to the padding area
-  // inserted after the body.
+  // Some functions have a jump over linker-inserted code or padding after the
+  // body. On AArch64, accept the skipped range only when it contains an
+  // erratum 843419 helper discovered earlier.
   auto isSkipJump = [&](const MCInst &Instr) {
-    if (!isX86())
-      return false;
     uint64_t TargetAddress = 0;
-    if (MIB->isUnconditionalBranch(Instr) &&
-        MIB->evaluateBranch(Instr, InstrAddress, InstrSize, TargetAddress)) {
-      if (TargetAddress >= InstrAddress + InstrSize &&
-          TargetAddress <= BF.getAddress() + BF.getMaxSize()) {
-        return true;
-      }
-    }
-    return false;
+    if (!MIB->isUnconditionalBranch(Instr) ||
+        !MIB->evaluateBranch(Instr, InstrAddress, InstrSize, TargetAddress) ||
+        TargetAddress < InstrAddress + InstrSize)
+      return false;
+
+    if (isX86())
+      return TargetAddress <= BF.getAddress() + BF.getMaxSize();
+    if (!isAArch64() || !RecoverRelocations)
+      return false;
+
+    auto TargetSection = getSectionForAddress(TargetAddress);
+    if (!TargetSection || &*TargetSection != BF.getOriginSection())
+      return false;
+
+    // GNU ld places a forward branch before an erratum veneer island. Stripped
+    // function discovery registers the veneer before padding validation, so a
+    // matching function in the skipped range identifies this layout.
+    auto NextFunction = BinaryFunctions.upper_bound(InstrAddress);
+    return NextFunction != BinaryFunctions.end() &&
+           NextFunction->first < TargetAddress &&
+           NextFunction->second.getOneName().starts_with("__BOLT_e843419_");
   };
 
   // For veneers that are not already covered by binary functions, only those
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 99d6d27264fbe..a9f7e6f6e995a 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -2329,6 +2329,9 @@ Error BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) {
     const uint32_t Offset = I->first;
     MCInst &Instr = I->second;
 
+    if (BC.RecoverRelocations && BC.isAArch64() && BC.MIB->isADRP(Instr))
+      MIB->setOffset(Instr, Offset);
+
     auto LI = Labels.find(Offset);
     if (LI != Labels.end()) {
       // Always create new BB at branch destination.
@@ -2570,7 +2573,8 @@ void BinaryFunction::postProcessCFG() {
   if (!requiresPreciseAddressMap() && !opts::Instrument) {
     for (BinaryBasicBlock &BB : blocks())
       for (MCInst &Inst : BB)
-        BC.MIB->clearOffset(Inst);
+        if (!(BC.RecoverRelocations && BC.isAArch64() && BC.MIB->isADRP(Inst)))
+          BC.MIB->clearOffset(Inst);
   }
 
   assert((!isSimple() || validateCFG()) &&
diff --git a/bolt/lib/Passes/CMakeLists.txt b/bolt/lib/Passes/CMakeLists.txt
index 686dee6987a73..65059ad90435d 100644
--- a/bolt/lib/Passes/CMakeLists.txt
+++ b/bolt/lib/Passes/CMakeLists.txt
@@ -11,6 +11,7 @@ add_llvm_library(LLVMBOLTPasses
   DataflowInfoManager.cpp
   FrameAnalysis.cpp
   FrameOptimizer.cpp
+  RelocationRecovery.cpp
   FixRelaxationPass.cpp
   FixRISCVCallsPass.cpp
   HFSort.cpp
diff --git a/bolt/lib/Passes/RelocationRecovery.cpp b/bolt/lib/Passes/RelocationRecovery.cpp
new file mode 100644
index 0000000000000..6cb1dfbbf7f22
--- /dev/null
+++ b/bolt/lib/Passes/RelocationRecovery.cpp
@@ -0,0 +1,361 @@
+//===- bolt/Passes/RelocationRecovery.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Passes/RelocationRecovery.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/DataExtractor.h"
+
+using namespace llvm;
+
+namespace opts {
+extern cl::OptionCategory BoltCategory;
+extern cl::opt<bool> Instrument;
+cl::opt<bool> AggressiveRelocationRecovery(
+    "aggressive-relocation-recovery",
+    cl::desc("also recover function pointers from general data sections; "
+             "requires --recover-relocations"),
+    cl::Hidden, cl::cat(BoltCategory));
+} // namespace opts
+
+namespace llvm {
+namespace bolt {
+namespace {
+
+/// Information collected for one ADRP instruction before rewriting it.
+/// Page is the absolute page computed by the original ADRP. Adds contains the
+/// ADD instructions that use this ADRP result to form the same target address;
+/// Symbol names that target when it can be recovered unambiguously.
+struct AdrpDefinition {
+  MCInst *ADRP;
+  uint64_t Page;
+  MCSymbol *Symbol = nullptr;
+  SmallVector<MCInst *, 4> Adds;
+  bool ClearOffsetAfterRecovery;
+};
+
+Error recoveryError(const BinaryFunction &BF, StringRef Reason) {
+  return createFatalBOLTError(Twine("cannot recover references in ") +
+                              BF.getPrintName() + ": " + Reason);
+}
+
+/// Follow an ADRP definition through the CFG and collect ADD uses that compute
+/// one target. Record all definitions and uses before rewriting operands
+/// because multiple definitions can reach the same ADD. When the target is not
+/// unique, preserve the input page in the ADRP and leave the ADD unchanged.
+Error collectAdrpDefUseChains(
+    BinaryFunction &BF, SmallVectorImpl<AdrpDefinition> &Definitions,
+    DenseMap<MCInst *, BinaryBasicBlock *> &AddBlocks) {
+  BinaryContext &BC = BF.getBinaryContext();
+  for (BinaryBasicBlock &BB : BF) {
+    for (auto I = BB.begin(); I != BB.end(); ++I) {
+      MCInst &ADRP = *I;
+      if (!BC.MIB->isADRP(ADRP) || !ADRP.getOperand(1).isImm())
+        continue;
+      const std::optional<uint32_t> Offset = BC.MIB->getOffset(ADRP);
+      if (!Offset)
+        return recoveryError(BF, "missing ADRP input offset");
+      const uint64_t Page = ((BF.getAddress() + *Offset) & ~uint64_t(4095)) +
+                            uint64_t(ADRP.getOperand(1).getImm()) * 4096;
+      AdrpDefinition Definition{&ADRP,
+                                Page,
+                                nullptr,
+                                {},
+                                !BF.requiresPreciseAddressMap() &&
+                                    !opts::Instrument};
+      const unsigned Reg = ADRP.getOperand(0).getReg();
+      SmallVector<BinaryBasicBlock *, 8> Worklist;
+      SmallPtrSet<BinaryBasicBlock *, 8> Visited;
+      Worklist.push_back(&BB);
+      bool First = true;
+      bool Complete = true;
+      std::optional<uint64_t> Target;
+      while (!Worklist.empty()) {
+        BinaryBasicBlock *Current = Worklist.pop_back_val();
+        auto Begin = First ? std::next(I) : Current->begin();
+        const bool IsInitial = First;
+        First = false;
+        if (!IsInitial && !Visited.insert(Current).second)
+          continue;
+        bool Killed = false;
+        for (auto II = Begin; II != Current->end(); ++II) {
+          MCInst &Inst = *II;
+          if (BC.MIB->isPseudo(Inst))
+            continue;
+          if (BC.MIB->isCall(Inst)) {
+            Complete = false;
+            Killed = true;
+            break;
+          }
+          const bool Uses = BC.MIB->hasUseOfPhysReg(Inst, Reg);
+          const bool Defines = BC.MIB->hasDefOfPhysReg(Inst, Reg);
+          if (Uses) {
+            if (!BC.MIB->isAddXri(Inst) || Inst.getOperand(1).getReg() != Reg ||
+                !Inst.getOperand(2).isImm() || !Inst.getOperand(3).isImm() ||
+                Inst.getOperand(3).getImm()) {
+              Complete = false;
+            } else {
+              const uint64_t Address = Page + Inst.getOperand(2).getImm();
+              if (Target && *Target != Address)
+                Complete = false;
+              Target = Address;
+              Definition.Adds.push_back(&Inst);
+              AddBlocks.try_emplace(&Inst, Current);
+            }
+          }
+          if (Defines) {
+            Killed = true;
+            break;
+          }
+        }
+        if (!Killed)
+          for (BinaryBasicBlock *Succ : Current->successors())
+            Worklist.push_back(Succ);
+      }
+      if (Target) {
+        if (BinaryFunction *Dest = BC.getBinaryFunctionAtAddress(*Target)) {
+          Definition.Symbol = Dest->getSymbol();
+        } else if (BinaryFunction *Dest =
+                       BC.getBinaryFunctionContainingAddress(*Target)) {
+          if (!Dest->isInConstantIsland(*Target)) {
+            if (const BinaryBasicBlock *Entry =
+                    Dest->getBasicBlockAtOffset(*Target - Dest->getAddress()))
+              Definition.Symbol = Dest->getSecondaryEntryPointSymbol(*Entry);
+          }
+        } else if (BC.getJumpTableContainingAddress(*Target)) {
+          Definition.Symbol =
+              BC.getOrCreateGlobalSymbol(*Target, "JUMP_TABLE/");
+        }
+      }
+      if (!Complete)
+        Definition.Symbol = nullptr;
+      Definitions.push_back(std::move(Definition));
+    }
+  }
+  return Error::success();
+}
+
+/// Walk backward from Use through its predecessor blocks and find the
+/// definition of Reg on every path. Succeed only when each path reaches an ADRP
+/// before a call, a non-ADRP definition of Reg, or a function-entry boundary.
+/// Scan UseBlock only before Use; if a loop reaches it again, scan the complete
+/// block. Each complete predecessor block is visited at most once.
+bool collectReachingAdrpDefinitions(
+    BinaryContext &BC, BinaryBasicBlock &UseBlock, MCInst &Use, unsigned Reg,
+    SmallPtrSetImpl<MCInst *> &ReachingDefinitions) {
+  struct BlockPosition {
+    BinaryBasicBlock *Block;
+    MCInst *Stop;
+  };
+  SmallVector<BlockPosition, 8> Worklist{{&UseBlock, &Use}};
+  SmallPtrSet<BinaryBasicBlock *, 8> VisitedFullBlocks;
+
+  while (!Worklist.empty()) {
+    const BlockPosition Position = Worklist.pop_back_val();
+    BinaryBasicBlock *Block = Position.Block;
+    auto End = Block->end();
+    if (Position.Stop) {
+      End = llvm::find_if(*Block,
+                          [&](MCInst &Inst) { return &Inst == Position.Stop; });
+      if (End == Block->end())
+        return false;
+    } else if (!VisitedFullBlocks.insert(Block).second) {
+      continue;
+    }
+
+    bool FoundDefinition = false;
+    for (auto I = std::make_reverse_iterator(End), E = Block->rend(); I != E;
+         ++I) {
+      MCInst &Inst = *I;
+      if (BC.MIB->isPseudo(Inst))
+        continue;
+      if (BC.MIB->isCall(Inst))
+        return false;
+      if (!BC.MIB->hasDefOfPhysReg(Inst, Reg))
+        continue;
+      if (!BC.MIB->isADRP(Inst) || Inst.getOperand(0).getReg() != Reg ||
+          !Inst.getOperand(1).isImm())
+        return false;
+      ReachingDefinitions.insert(&Inst);
+      FoundDefinition = true;
+      break;
+    }
+    if (FoundDefinition)
+      continue;
+    if (Block->isEntryPoint() || Block->isLandingPad() || Block->pred_empty())
+      return false;
+    for (BinaryBasicBlock *Pred : Block->predecessors())
+      Worklist.push_back({Pred, nullptr});
+  }
+  return !ReachingDefinitions.empty();
+}
+
+Error recoverAArch64InstructionReferences(BinaryContext &BC) {
+  SmallVector<AdrpDefinition, 16> Definitions;
+  DenseMap<MCInst *, BinaryBasicBlock *> AddBlocks;
+  for (auto &BFI : BC.getBinaryFunctions()) {
+    BinaryFunction &BF = BFI.second;
+    if (!BC.shouldEmit(BF))
+      continue;
+    if (Error E = collectAdrpDefUseChains(BF, Definitions, AddBlocks))
+      return E;
+  }
+
+  DenseMap<MCInst *, AdrpDefinition *> DefinitionsByInstruction;
+  SmallPtrSet<MCInst *, 16> CandidateAdds;
+  for (AdrpDefinition &Definition : Definitions) {
+    DefinitionsByInstruction.try_emplace(Definition.ADRP, &Definition);
+    CandidateAdds.insert_range(Definition.Adds);
+  }
+
+  DenseMap<MCInst *, MCSymbol *> CandidateAddTargets;
+  SmallPtrSet<MCInst *, 16> UnsafeAdds;
+  for (MCInst *Add : CandidateAdds) {
+    auto BlockIt = AddBlocks.find(Add);
+    if (BlockIt == AddBlocks.end()) {
+      UnsafeAdds.insert(Add);
+      continue;
+    }
+
+    const unsigned Reg = Add->getOperand(1).getReg();
+    SmallPtrSet<MCInst *, 4> ReachingDefinitions;
+    if (!collectReachingAdrpDefinitions(BC, *BlockIt->second, *Add, Reg,
+                                        ReachingDefinitions)) {
+      UnsafeAdds.insert(Add);
+      continue;
+    }
+
+    std::optional<uint64_t> Page;
+    MCSymbol *Symbol = nullptr;
+    bool Valid = true;
+    for (MCInst *ADRP : ReachingDefinitions) {
+      auto DefinitionIt = DefinitionsByInstruction.find(ADRP);
+      if (DefinitionIt == DefinitionsByInstruction.end() ||
+          !DefinitionIt->second->Symbol ||
+          !llvm::is_contained(DefinitionIt->second->Adds, Add)) {
+        Valid = false;
+        break;
+      }
+      const AdrpDefinition &Definition = *DefinitionIt->second;
+      if ((Page && *Page != Defin...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/221991


More information about the llvm-commits mailing list