[llvm] [RFC][BOLT] Add a new parallel DWARF processing(2/2) (PR #197859)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 11 01:56:24 PDT 2026


https://github.com/Thrrreeee updated https://github.com/llvm/llvm-project/pull/197859

>From 484f2c16c37c9c3100aa421852a9cac2647f3060 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Tue, 26 May 2026 12:35:55 +0800
Subject: [PATCH 01/10] [RFC][BOLT] A New Parallel DWARF Processing Approach in
 BOLT (2/2)

---
 bolt/include/bolt/Core/DIEBuilder.h           |   8 +-
 bolt/include/bolt/Core/DebugData.h            |  26 +-
 bolt/include/bolt/Rewrite/DWARFRewriter.h     |  60 +-
 bolt/lib/Core/DIEBuilder.cpp                  |  19 +
 bolt/lib/Core/DebugData.cpp                   |  45 +-
 bolt/lib/Rewrite/DWARFRewriter.cpp            | 692 +++++++++++++-----
 ...f4-cross-cu-backward-different-abbrev.test |   2 +-
 bolt/test/X86/dwarf4-cross-cu-ranges.test     |  55 ++
 8 files changed, 680 insertions(+), 227 deletions(-)
 create mode 100644 bolt/test/X86/dwarf4-cross-cu-ranges.test

diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h
index 628edfc12fdaf..72404e6816e11 100644
--- a/bolt/include/bolt/Core/DIEBuilder.h
+++ b/bolt/include/bolt/Core/DIEBuilder.h
@@ -341,7 +341,13 @@ class DIEBuilder {
   void generateAbbrevs();
   void generateUnitAbbrevs(DIE *Die);
   void assignAbbrev(DIEAbbrev &Abbrev);
-
+  void syncAbbrevTableFrom(const DIEBuilder &SrcBuilder);
+  /// Set the base offset for CU emission, used by the incremental merge
+  /// pipeline
+  void setUnitOffsetBases(uint64_t Base) {
+    DebugNamesUnitSize = Base;
+    UnitSize = Base;
+  }
   /// Finish current DIE construction.
   void finish();
 
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index faf7bb62c6bee..f1120c17fee9f 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -571,6 +571,13 @@ class DebugLocWriter {
   /// Offset of an empty location list.
   static constexpr uint32_t EmptyListOffset = 0;
 
+  /// Returns the current size (in bytes) of the serialized location buffer.
+  uint64_t getLocBufferSize() const { return LocBuffer->size(); }
+
+  /// Applies an additional base offset to all non-empty location list offsets
+  /// recorded for this CU.
+  void applyBase(DIEBuilder &DIEBldr, uint64_t Base);
+
   LocWriterKind getKind() const { return Kind; }
 
   static bool classof(const DebugLocWriter *Writer) {
@@ -580,11 +587,6 @@ class DebugLocWriter {
 protected:
   std::unique_ptr<DebugBufferVector> LocBuffer;
   std::unique_ptr<raw_svector_ostream> LocStream;
-  /// Current offset in the section (updated as new entries are written).
-  /// Starts with 0 here since this only writes part of a full location lists
-  /// section. In the final section, for DWARF4, the first 16 bytes are reserved
-  /// for an empty list.
-  static uint32_t LocSectionOffset;
   uint8_t DwarfVersion{4};
   LocWriterKind Kind{LocWriterKind::DebugLocWriter};
 
@@ -600,6 +602,14 @@ class DebugLocWriter {
   /// The list of debug info patches to be made once individual
   /// location list writers have been filled
   VectorLocListDebugInfoPatchType LocListDebugInfoPatches;
+  /// Location list attribute patches pending base-address relocation.
+  struct LocListPatch {
+    DIE *Die;
+    dwarf::Attribute Attr;
+    dwarf::Form Form;
+  };
+  /// Accumulated location list patches awaiting base-address adjustment.
+  std::vector<LocListPatch> LocListPatches;
 };
 
 class DebugLoclistWriter : public DebugLocWriter {
@@ -613,11 +623,6 @@ class DebugLoclistWriter : public DebugLocWriter {
     if (DwarfVersion >= 5) {
       LocBodyBuffer = std::make_unique<DebugBufferVector>();
       LocBodyStream = std::make_unique<raw_svector_ostream>(*LocBodyBuffer);
-    } else {
-      // Writing out empty location list to which all references to empty
-      // location lists will point.
-      const char Zeroes[16] = {0};
-      *LocStream << StringRef(Zeroes, 16);
     }
   }
 
@@ -657,7 +662,6 @@ class DebugLoclistWriter : public DebugLocWriter {
   std::unique_ptr<raw_svector_ostream> LocBodyStream;
   std::vector<uint32_t> RelativeLocListOffsets;
   uint32_t NumberOfEntries{0};
-  static uint32_t LoclistBaseOffset;
 };
 
 /// Abstract interface for classes that apply modifications to a binary string.
diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h
index cab346b5aebc5..87cdca0ab7ad9 100644
--- a/bolt/include/bolt/Rewrite/DWARFRewriter.h
+++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h
@@ -16,6 +16,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/CodeGen/DIE.h"
 #include "llvm/MC/MCContext.h"
+#include "llvm/Support/ThreadPool.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include <cstdint>
 #include <memory>
@@ -40,12 +41,31 @@ class DWARFRewriter {
     uint64_t TypeHash;
     uint64_t TypeDIERelativeOffset;
   };
+  struct BucketLocAccumOffset {
+    uint64_t LoclistsOffset = 0;
+    uint64_t LegacyLocOffset = 0;
+    std::vector<uint64_t> LocListCUOrder;
+  };
+  struct BucketLocalWriter {
+    std::unique_ptr<DebugRangeListsSectionWriter> RngListsWriter;
+    std::unique_ptr<DebugRangesSectionWriter> LegacyRangesWriter;
+
+    /// Release whichever writer was initialized.
+    void clear() {
+      if (RngListsWriter)
+        RngListsWriter.reset();
+      if (LegacyRangesWriter)
+        LegacyRangesWriter.reset();
+    }
+  };
 
 private:
   BinaryContext &BC;
 
   std::mutex DWARFRewriterMutex;
 
+  std::unique_ptr<llvm::ThreadPoolInterface> DebugInfoThreadPool;
+
   /// Stores and serializes information that will be put into the
   /// .debug_ranges DWARF section.
   std::unique_ptr<DebugRangesSectionWriter> LegacyRangesSectionWriter;
@@ -86,6 +106,7 @@ class DWARFRewriter {
       AddressWritersByCU;
 
   std::mutex LocListDebugInfoPatchesMutex;
+  std::mutex DebugNamesUpdateMutex;
 
   std::unordered_map<DWARFUnit *, uint64_t> LineTablePatchMap;
   std::unordered_map<const DWARFUnit *, uint64_t> TypeUnitRelocMap;
@@ -100,6 +121,8 @@ class DWARFRewriter {
   /// Used to track last CU offset for GDB Index.
   uint32_t CUOffset{0};
 
+  llvm::ThreadPoolInterface &
+  getOrCreateDebugInfoThreadPool(unsigned ThreadCount);
   /// Update debug info for all DIEs in \p Unit.
   void updateUnitDebugInfo(DWARFUnit &Unit, DIEBuilder &DIEBldr,
                            DebugLocWriter &DebugLocWriter,
@@ -118,19 +141,30 @@ class DWARFRewriter {
   ///    attribute.
   void updateDWARFObjectAddressRanges(
       DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die,
-      uint64_t DebugRangesOffset,
+      uint64_t DebugRangesOffset, DebugAddrWriter &AddressWriter,
       std::optional<uint64_t> RangesBase = std::nullopt);
 
   std::unique_ptr<DebugBufferVector>
-  makeFinalLocListsSection(DWARFVersion Version);
-
+  makeFinalLocListsSection(DWARFVersion Version, ArrayRef<uint64_t> CUOrder);
+  void createRangeLocListAndAddressWriters();
   /// Finalize type sections in the main binary.
   CUOffsetMap finalizeTypeSections(DIEBuilder &DIEBlder, DIEStreamer &Streamer,
                                    GDBIndex &GDBIndexSection);
 
+  /// Finalize str section in the dwo
+  void finalizeSkeletonAndStrSection(
+      DIEBuilder &PartDIEBlder, DWARFUnit &CU,
+      const std::unordered_map<uint64_t, std::string> &DWOToNameMap);
+
+  /// Merge Bucket locs section and ranges section result
+  void mergePerBucketLocsAndRanges(
+      DIEBuilder &PartDIEBlder, BucketLocalWriter &LocalWriters,
+      const std::unordered_map<uint64_t, std::string> &DWOToNameMap,
+      BucketLocAccumOffset &Accum);
+
   /// Process and write out CUs that are passed in.
-  void finalizeCompileUnits(DIEBuilder &DIEBlder, DIEStreamer &Streamer,
-                            CUOffsetMap &CUMap,
+  void finalizeCompileUnits(DIEBuilder &DIEBlder, DIEBuilder &MainDIEBlder,
+                            DIEStreamer &Streamer, CUOffsetMap &CUMap,
                             const std::list<DWARFUnit *> &CUs,
                             DebugAddrWriter &FinalAddrWriter);
 
@@ -139,7 +173,19 @@ class DWARFRewriter {
                              DWARF5AcceleratorTable &DebugNamesTable,
                              DIEStreamer &Streamer, raw_svector_ostream &ObjOS,
                              CUOffsetMap &CUMap,
-                             DebugAddrWriter &FinalAddrWriter);
+                             DebugAddrWriter &FinalAddrWriter,
+                             std::vector<uint64_t> SortedCU);
+
+  /// Process CUs in main binary.
+  void processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder,
+                           BucketLocalWriter &LocalWriter);
+
+  void processBucket(size_t Idx, std::vector<std::vector<DWARFUnit *>> &PartVec,
+                     std::vector<std::unique_ptr<DIEBuilder>> &BucketDIEBlders,
+                     std::vector<BucketLocalWriter> &LocalWriters,
+                     DWARF5AcceleratorTable &DebugNamesTable,
+                     std::unordered_map<uint64_t, std::string> &DWOToNameMap,
+                     GDBIndex &GDBIndexSection);
 
   /// Patches the binary for DWARF address ranges (e.g. in functions and lexical
   /// blocks) to be updated.
@@ -168,7 +214,7 @@ class DWARFRewriter {
   void convertToRangesPatchDebugInfo(
       DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die,
       uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo,
-      DIEValue &HighPCAttrInfo,
+      DIEValue &HighPCAttrInfo, DebugAddrWriter &AddressWriter,
       std::optional<uint64_t> RangesBase = std::nullopt);
 
 public:
diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp
index 0957071cb117f..4a2b668236a9f 100644
--- a/bolt/lib/Core/DIEBuilder.cpp
+++ b/bolt/lib/Core/DIEBuilder.cpp
@@ -1040,6 +1040,25 @@ void DIEBuilder::generateUnitAbbrevs(DIE *Die) {
   }
 }
 
+void DIEBuilder::syncAbbrevTableFrom(const DIEBuilder &SrcBuilder) {
+  Abbreviations.clear();
+  AbbreviationsSet.clear();
+  Abbreviations.reserve(SrcBuilder.Abbreviations.size());
+  for (const auto &SrcAbbrev : SrcBuilder.Abbreviations) {
+    auto Copy = std::make_unique<DIEAbbrev>(SrcAbbrev->getTag(),
+                                            SrcAbbrev->hasChildren());
+    for (const auto &Attr : SrcAbbrev->getData())
+      Copy->AddAttribute(Attr.getAttribute(), Attr.getForm());
+    Copy->setNumber(SrcAbbrev->getNumber());
+    FoldingSetNodeID ID;
+    Copy->Profile(ID);
+    void *InsertToken;
+    if (!AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken))
+      AbbreviationsSet.InsertNode(Copy.get(), InsertToken);
+    Abbreviations.push_back(std::move(Copy));
+  }
+}
+
 static uint64_t getHash(const DWARFUnit &DU) {
   // Before DWARF5 TU units are in their own section, so at least one offset,
   // first one, will be the same as CUs in .debug_info.dwo section
diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp
index 971720c918687..db51343908717 100644
--- a/bolt/lib/Core/DebugData.cpp
+++ b/bolt/lib/Core/DebugData.cpp
@@ -564,14 +564,12 @@ void DebugLocWriter::init() {
   LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer);
   // Writing out empty location list to which all references to empty location
   // lists will point.
-  if (!LocSectionOffset && DwarfVersion < 5) {
+  if (DwarfVersion < 5) {
     const char Zeroes[16] = {0};
     *LocStream << StringRef(Zeroes, 16);
-    LocSectionOffset += 16;
   }
 }
 
-uint32_t DebugLocWriter::LocSectionOffset = 0;
 void DebugLocWriter::addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
                              DebugLocationsVector &LocList) {
   if (LocList.empty()) {
@@ -581,7 +579,7 @@ void DebugLocWriter::addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
   }
   // Since there is a separate DebugLocWriter for each thread,
   // we don't need a lock to read the SectionOffset and update it.
-  const uint32_t EntryOffset = LocSectionOffset;
+  const uint32_t EntryOffset = LocBuffer->size();
 
   for (const DebugLocationEntry &Entry : LocList) {
     support::endian::write(*LocStream, static_cast<uint64_t>(Entry.LowPC),
@@ -592,13 +590,11 @@ void DebugLocWriter::addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
                            llvm::endianness::little);
     *LocStream << StringRef(reinterpret_cast<const char *>(Entry.Expr.data()),
                             Entry.Expr.size());
-    LocSectionOffset += 2 * 8 + 2 + Entry.Expr.size();
   }
   LocStream->write_zeros(16);
-  LocSectionOffset += 16;
-  LocListDebugInfoPatches.push_back({0xdeadbeee, EntryOffset}); // never seen
-                                                                // use
+  LocListDebugInfoPatches.push_back({0xdeadbeee, EntryOffset});
   replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), EntryOffset);
+  LocListPatches.push_back({&Die, AttrInfo.getAttribute(), AttrInfo.getForm()});
 }
 
 std::unique_ptr<DebugBufferVector> DebugLocWriter::getBuffer() {
@@ -608,6 +604,27 @@ std::unique_ptr<DebugBufferVector> DebugLocWriter::getBuffer() {
 // DWARF 4: 2.6.2
 void DebugLocWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) {}
 
+/// Rebases all pending location list offsets by adding \p Base,
+/// updating the corresponding attributes in each patched DIE.
+void DebugLocWriter::applyBase(DIEBuilder &DIEBldr, uint64_t Base) {
+  if (Base == 0) {
+    LocListPatches.clear();
+    return;
+  }
+  for (const LocListPatch &LP : LocListPatches) {
+    if (!LP.Die)
+      continue;
+    DIEValue Cur = LP.Die->findAttribute(LP.Attr);
+    if (!Cur.getType())
+      continue;
+    uint64_t OldVal = Cur.getDIEInteger().getValue();
+    if (OldVal == DebugLocWriter::EmptyListOffset)
+      continue;
+    DIEBldr.replaceValue(LP.Die, LP.Attr, LP.Form, DIEInteger(OldVal + Base));
+  }
+  LocListPatches.clear();
+}
+
 static void writeEmptyListDwarf5(raw_svector_ostream &Stream) {
   support::endian::write(Stream, static_cast<uint32_t>(4),
                          llvm::endianness::little);
@@ -716,7 +733,6 @@ void DebugLoclistWriter::addList(DIEBuilder &DIEBldr, DIE &Die,
                        *LocBodyStream);
 }
 
-uint32_t DebugLoclistWriter::LoclistBaseOffset = 0;
 void DebugLoclistWriter::finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die) {
   if (LocBodyBuffer->empty()) {
     DIEValue LocListBaseAttrInfo =
@@ -751,18 +767,17 @@ void DebugLoclistWriter::finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die) {
   *LocStream << *LocBodyBuffer;
 
   if (!isSplitDwarf()) {
+    const uint64_t LocalBase = getDWARF5RngListLocListHeaderSize();
     DIEValue LocListBaseAttrInfo =
         Die.findAttribute(dwarf::DW_AT_loclists_base);
     if (LocListBaseAttrInfo.getType()) {
-      DIEBldr.replaceValue(
-          &Die, dwarf::DW_AT_loclists_base, LocListBaseAttrInfo.getForm(),
-          DIEInteger(LoclistBaseOffset + getDWARF5RngListLocListHeaderSize()));
+      DIEBldr.replaceValue(&Die, dwarf::DW_AT_loclists_base,
+                           LocListBaseAttrInfo.getForm(),
+                           DIEInteger(LocalBase));
     } else {
       DIEBldr.addValue(&Die, dwarf::DW_AT_loclists_base,
-                       dwarf::DW_FORM_sec_offset,
-                       DIEInteger(LoclistBaseOffset + Header->size()));
+                       dwarf::DW_FORM_sec_offset, DIEInteger(LocalBase));
     }
-    LoclistBaseOffset += LocBuffer->size();
   }
   clearList(RelativeLocListOffsets);
   clearList(*LocArrayBuffer);
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 076d2e3d29cce..1b8327b624444 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -14,6 +14,8 @@
 #include "bolt/Core/DynoStats.h"
 #include "bolt/Core/ParallelUtilities.h"
 #include "bolt/Rewrite/RewriteInstance.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/EquivalenceClasses.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -44,11 +46,17 @@
 #include "llvm/Support/ThreadPool.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
+#include <cassert>
+#include <condition_variable>
+#include <cstddef>
 #include <cstdint>
 #include <functional>
 #include <iterator>
+#include <limits>
 #include <memory>
+#include <mutex>
 #include <optional>
+#include <queue>
 #include <string>
 #include <unordered_map>
 #include <utility>
@@ -91,6 +99,19 @@ static void printDie(DWARFUnit &DU, uint64_t DIEOffset) {
 
 using namespace bolt;
 
+llvm::ThreadPoolInterface &
+DWARFRewriter::getOrCreateDebugInfoThreadPool(unsigned ThreadsCount) {
+  if (opts::NoThreads || ThreadsCount == 0)
+    ThreadsCount = 1;
+
+  if (DebugInfoThreadPool)
+    return *DebugInfoThreadPool;
+
+  DebugInfoThreadPool = std::make_unique<DefaultThreadPool>(
+      llvm::hardware_concurrency(ThreadsCount));
+  return *DebugInfoThreadPool;
+}
+
 /// Take a set of DWARF address ranges corresponding to the input binary and
 /// translate them to a set of address ranges in the output binary.
 static DebugAddressRangesVector
@@ -332,9 +353,14 @@ static cl::opt<bool> KeepARanges(
 static cl::opt<unsigned>
     DebugThreadCount("debug-thread-count",
                      cl::desc("specifies thread count for the multithreading "
-                              "for updating DWO debug info"),
+                              "for updating DWARF debug info"),
                      cl::init(1), cl::cat(BoltCategory));
 
+static cl::opt<bool>
+    PreserveOrder("dwarf-preserve-order",
+                  cl::desc("Update debug information in deterministic order"),
+                  cl::init(true), cl::cat(BoltCategory));
+
 static cl::opt<std::string> DwarfOutputPath(
     "dwarf-output-path",
     cl::desc("Path to where .dwo files will be written out to."), cl::init(""),
@@ -429,6 +455,45 @@ static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
   return false;
 }
 
+static void fixupDWARFRanges(DIEBuilder &DIEBlder, DWARFUnit &Unit,
+                             uint64_t Offset) {
+  if (Offset == 0)
+    return;
+
+  const uint16_t Version = Unit.getVersion();
+  if (Version >= 5) {
+    DIE *CUDie = DIEBlder.getUnitDIEbyUnit(Unit);
+    DIEValue RngBaseVal = CUDie->findAttribute(dwarf::DW_AT_rnglists_base);
+    if (RngBaseVal) {
+      uint64_t OldVal = RngBaseVal.getDIEInteger().getValue();
+      DIEBlder.replaceValue(CUDie, dwarf::DW_AT_rnglists_base,
+                            RngBaseVal.getForm(), DIEInteger(OldVal + Offset));
+    }
+  }
+
+  const auto &DIs = DIEBlder.getDIEsByUnit(Unit);
+  for (const auto &DI : DIs) {
+    DIE *Die = DI->Die;
+    DIEValue RangesVal = Die->findAttribute(dwarf::DW_AT_ranges);
+    if (RangesVal &&
+        (Version < 5 || RangesVal.getForm() == dwarf::DW_FORM_sec_offset)) {
+      uint64_t OldVal = RangesVal.getDIEInteger().getValue();
+      DIEBlder.replaceValue(Die, dwarf::DW_AT_ranges, RangesVal.getForm(),
+                            DIEInteger(OldVal + Offset));
+    }
+
+    if (Version < 5) {
+      DIEValue GnuBaseVal = Die->findAttribute(dwarf::DW_AT_GNU_ranges_base);
+      if (GnuBaseVal) {
+        uint64_t OldVal = GnuBaseVal.getDIEInteger().getValue();
+        DIEBlder.replaceValue(Die, dwarf::DW_AT_GNU_ranges_base,
+                              GnuBaseVal.getForm(),
+                              DIEInteger(OldVal + Offset));
+      }
+    }
+  }
+}
+
 static Expected<llvm::DWARFAddressRangesVector>
 getDIEAddressRanges(const DIE &Die, DWARFUnit &DU) {
   uint64_t LowPC, HighPC, Index;
@@ -535,43 +600,92 @@ static void emitDWOBuilder(const std::string &DWOName,
                          StrOffstsWriter, StrWriter, TempRangesSectionWriter);
 }
 
-using DWARFUnitVec = std::vector<DWARFUnit *>;
-using CUPartitionVector = std::vector<DWARFUnitVec>;
-/// Partitions CUs in to buckets. Bucket size is controlled by
-/// cu-processing-batch-size. All the CUs that have cross CU reference reference
-/// as a source are put in to the same initial bucket.
-static CUPartitionVector partitionCUs(DWARFContext &DwCtx) {
-  CUPartitionVector Vec(2);
-  unsigned Counter = 0;
-  const DWARFDebugAbbrev *Abbr = DwCtx.getDebugAbbrev();
-  for (std::unique_ptr<DWARFUnit> &CU : DwCtx.compile_units()) {
-    Expected<const DWARFAbbreviationDeclarationSet *> AbbrDeclSet =
-        Abbr->getAbbreviationDeclarationSet(CU->getAbbreviationsOffset());
-    if (!AbbrDeclSet) {
-      consumeError(AbbrDeclSet.takeError());
-      return Vec;
-    }
-    bool CrossCURefFound = false;
-    for (const DWARFAbbreviationDeclaration &Decl : *AbbrDeclSet.get()) {
-      for (const DWARFAbbreviationDeclaration::AttributeSpec &Attr :
-           Decl.attributes()) {
-        if (Attr.Form == dwarf::DW_FORM_ref_addr) {
-          CrossCURefFound = true;
+static std::vector<std::vector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
+  SmallVector<DWARFUnit *, 0> AllCUs;
+  for (auto &CU : DwCtx.compile_units())
+    AllCUs.push_back(CU.get());
+  if (AllCUs.empty())
+    return {};
+  auto FindCuForOffset = [&](uint64_t Offset) -> DWARFUnit * {
+    auto *It =
+        llvm::upper_bound(AllCUs, Offset, [](uint64_t Off, DWARFUnit *U) {
+          return Off < U->getOffset();
+        });
+    if (It == AllCUs.begin())
+      return nullptr;
+    DWARFUnit *TargetCU = *--It;
+    // Ensure offset falls within TargetCU's range.
+    if (Offset >= TargetCU->getNextUnitOffset())
+      return nullptr;
+    return TargetCU;
+  };
+
+  DenseSet<DWARFUnit *> CrossRefSet;
+  EquivalenceClasses<DWARFUnit *> EC;
+  for (DWARFUnit *CU : AllCUs) {
+    const DWARFAbbreviationDeclarationSet *AbbrevSet = CU->getAbbreviations();
+    if (!AbbrevSet)
+      continue;
+    SmallDenseSet<const DWARFAbbreviationDeclaration *, 4> RefAddrAbbrevs;
+    for (const auto &Decl : *AbbrevSet)
+      for (const auto &Spec : Decl.attributes())
+        if (Spec.Form == dwarf::DW_FORM_ref_addr) {
+          RefAddrAbbrevs.insert(&Decl);
           break;
         }
+    if (RefAddrAbbrevs.empty())
+      continue;
+    // Track CUs involved in cross-CU references via DW_FORM_ref_addr.
+    for (const DWARFDebugInfoEntry &Entry : CU->dies()) {
+      DWARFDie Die(CU, &Entry);
+      const DWARFAbbreviationDeclaration *Abbrev =
+          Die.getAbbreviationDeclarationPtr();
+      if (!Abbrev || !RefAddrAbbrevs.count(Abbrev))
+        continue;
+      for (const DWARFAttribute &Attr : Die.attributes()) {
+        if (Attr.Value.getForm() != dwarf::DW_FORM_ref_addr)
+          continue;
+        auto OptRef = Attr.Value.getAsDebugInfoReference();
+        if (!OptRef)
+          continue;
+        DWARFUnit *TargetCU = FindCuForOffset(*OptRef);
+        if (!TargetCU)
+          continue;
+        if (CrossRefSet.insert(CU).second)
+          EC.insert(CU);
+        if (CrossRefSet.insert(TargetCU).second)
+          EC.insert(TargetCU);
+        EC.unionSets(CU, TargetCU);
       }
-      if (CrossCURefFound)
-        break;
     }
-    if (CrossCURefFound) {
-      Vec[0].push_back(CU.get());
-    } else {
-      ++Counter;
-      Vec.back().push_back(CU.get());
+  }
+
+  DenseMap<DWARFUnit *, std::vector<DWARFUnit *>> MembersByLeader;
+  for (DWARFUnit *CU : AllCUs) {
+    if (!CrossRefSet.count(CU))
+      continue;
+    MembersByLeader[EC.getLeaderValue(CU)].push_back(CU);
+  }
+
+  std::vector<DWARFUnit *> Leaders;
+  Leaders.reserve(MembersByLeader.size());
+  for (auto &[Leader, Members] : MembersByLeader)
+    Leaders.push_back(Leader);
+  llvm::sort(Leaders, [&](DWARFUnit *A, DWARFUnit *B) {
+    return MembersByLeader[A].front()->getOffset() <
+           MembersByLeader[B].front()->getOffset();
+  });
+
+  // Emit cross-ref buckets, then singleton non-cross-ref CUs.
+  std::vector<std::vector<DWARFUnit *>> Vec;
+  for (DWARFUnit *Leader : Leaders)
+    Vec.push_back(std::move(MembersByLeader[Leader]));
+  for (DWARFUnit *CU : AllCUs) {
+    if (!CrossRefSet.count(CU)) {
+      Vec.push_back({CU});
     }
-    if (Counter % opts::BatchSize == 0 && !Vec.back().empty())
-      Vec.push_back({});
   }
+
   return Vec;
 }
 
@@ -610,45 +724,137 @@ getDWONameMap(DWARFContext &DwCtx) {
   }
   return DWOIDToNameMap;
 }
-void DWARFRewriter::updateDebugInfo() {
-  ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info");
-  if (!DebugInfo)
+
+void DWARFRewriter::finalizeSkeletonAndStrSection(
+    DIEBuilder &PartDIEBlder, DWARFUnit &CU,
+    const std::unordered_map<uint64_t, std::string> &DWOToNameMap) {
+  const std::optional<uint64_t> DWOId = CU.getDWOId();
+  std::optional<DWARFUnit *> DWOCU = DWOId ? BC.getDWOCU(*DWOId) : std::nullopt;
+  const bool HasSplitCU = DWOCU && *DWOCU != nullptr;
+  const unsigned Version = CU.getVersion();
+
+  if (HasSplitCU) {
+    auto It = DWOToNameMap.find(*DWOId);
+    if (It != DWOToNameMap.end()) {
+      PartDIEBlder.updateDWONameCompDir(*StrOffstsWriter, *StrWriter, CU,
+                                        opts::DwarfOutputPath,
+                                        StringRef(It->second));
+      if (Version >= 5 && StrOffstsWriter->isStrOffsetsSectionModified())
+        StrOffstsWriter->finalizeSection(CU, PartDIEBlder);
+    }
+    //  split CU were just finalized above when the
+    // .debug_str_offsets section was modified;
     return;
+  }
 
-  ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
-  StrWriter = std::make_unique<DebugStrWriter>(*BC.DwCtx, false);
-  StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(BC);
+  if (Version >= 5)
+    StrOffstsWriter->finalizeSection(CU, PartDIEBlder);
+}
 
-  /// Stores and serializes information that will be put into the
-  /// .debug_addr DWARF section.
-  std::unique_ptr<DebugAddrWriter> FinalAddrWriter;
+/// Merge: compute per-CU loclist/legacy-loc bases and in the
+/// same loop apply DWARF5 rnglists/loclists base fixups and legacy
+/// .debug_loc base fixups.
+void DWARFRewriter::mergePerBucketLocsAndRanges(
+    DIEBuilder &PartDIEBlder, BucketLocalWriter &LocalWriters,
+    const std::unordered_map<uint64_t, std::string> &DWOToNameMap,
+    BucketLocAccumOffset &Accum) {
+  const auto &Processed = PartDIEBlder.getProcessedCUs();
+  std::vector<DWARFUnit *> SortedCUs;
+  SortedCUs.assign(Processed.begin(), Processed.end());
+  // Ensure deterministic output by sorting CUs in section offset order.
+  llvm::sort(SortedCUs, [](const DWARFUnit *A, const DWARFUnit *B) {
+    return A->getOffset() < B->getOffset();
+  });
+
+  for (DWARFUnit *CU : SortedCUs) {
+    // record CUs order to make loc/loclist order correct
+    Accum.LocListCUOrder.push_back(CU->getOffset());
+    finalizeSkeletonAndStrSection(PartDIEBlder, *CU, DWOToNameMap);
+  }
+  for (DWARFUnit *CU : SortedCUs) {
+
+    const uint64_t CUOffset = CU->getOffset();
+    // Compute this CU's base and adjust loclists / legacy loc fixups inline.
+    uint64_t LoclistsBase = 0;
+    uint64_t LegacyLocBase = 0;
+
+    auto LocIt = LocListWritersByCU.find(CUOffset);
+    if (LocIt != LocListWritersByCU.end()) {
+      DebugLocWriter *LocWriter = LocIt->second.get();
+      auto *LocListWriter = dyn_cast<DebugLoclistWriter>(LocWriter);
+      const uint64_t BufferSize = LocWriter->getLocBufferSize();
+
+      if (LocListWriter && LocListWriter->getDwarfVersion() >= 5 &&
+          !LocListWriter->isSplitDwarf() && BufferSize != 0) {
+        LoclistsBase = Accum.LoclistsOffset;
+        Accum.LoclistsOffset += BufferSize;
+      } else if (!LocListWriter) {
+        LegacyLocBase = Accum.LegacyLocOffset;
+        // DWARF4 .debug_loc buffers carry a trailing 16-byte terminator
+        // which is written only once at the global level; discount it here
+        // so the next CU's base offset is computed correctly.
+        if (BufferSize > 16)
+          Accum.LegacyLocOffset += BufferSize - 16;
+      }
+    }
 
-  if (BC.isDWARF5Used()) {
-    FinalAddrWriter = std::make_unique<DebugAddrWriterDwarf5>(&BC);
-    RangeListsSectionWriter = std::make_unique<DebugRangeListsSectionWriter>();
-  } else {
-    FinalAddrWriter = std::make_unique<DebugAddrWriter>(&BC);
+    if (CU->getVersion() >= 5) {
+      uint64_t RangsOffset = RangeListsSectionWriter->getSectionOffset();
+      fixupDWARFRanges(PartDIEBlder, *CU, RangsOffset);
+
+      if (LoclistsBase) {
+        DIE *UnitDIE = PartDIEBlder.getUnitDIEbyUnit(*CU);
+        if (UnitDIE) {
+          DIEValue LocBase = UnitDIE->findAttribute(dwarf::DW_AT_loclists_base);
+          if (LocBase.getType()) {
+            const uint64_t OldVal = LocBase.getDIEInteger().getValue();
+            PartDIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_loclists_base,
+                                      LocBase.getForm(),
+                                      DIEInteger(OldVal + LoclistsBase));
+          }
+        }
+      }
+      continue;
+    }
+
+    if (LegacyLocBase && LocIt != LocListWritersByCU.end())
+      LocIt->second->applyBase(PartDIEBlder, LegacyLocBase);
   }
 
-  if (BC.isDWARFLegacyUsed()) {
-    LegacyRangesSectionWriter = std::make_unique<DebugRangesSectionWriter>();
-    LegacyRangesSectionWriter->initSection();
+  /// Append this bucket's .debug_rnglists and .debug_ranges local buffers to
+  /// the global writers.
+  if (LocalWriters.RngListsWriter && RangeListsSectionWriter) {
+    std::unique_ptr<DebugBufferVector> LocalBuf =
+        LocalWriters.RngListsWriter->releaseBuffer();
+    RangeListsSectionWriter->appendToRangeBuffer(*LocalBuf);
   }
 
-  uint32_t CUIndex = 0;
-  std::mutex AccessMutex;
-  // Needs to be invoked in the same order as CUs are processed.
-  llvm::DenseMap<uint64_t, uint64_t> LocListWritersIndexByCU;
-  auto createRangeLocListAddressWriters = [&](DWARFUnit &CU) {
-    std::lock_guard<std::mutex> Lock(AccessMutex);
+  if (!LocalWriters.LegacyRangesWriter || !LegacyRangesSectionWriter)
+    return;
+
+  std::unique_ptr<DebugBufferVector> LegacyBuf =
+      LocalWriters.LegacyRangesWriter->releaseBuffer();
+  // emission order is established later by emitBucketCompileUnits
+  for (DWARFUnit *CU : SortedCUs)
+    if (CU->getVersion() <= 4)
+      fixupDWARFRanges(PartDIEBlder, *CU,
+                       LegacyRangesSectionWriter->getSectionOffset());
+  LegacyRangesSectionWriter->appendToRangeBuffer(*LegacyBuf);
+}
+
+void DWARFRewriter::createRangeLocListAndAddressWriters() {
+  for (auto &DWCU : BC.DwCtx->compile_units()) {
+    DWARFUnit &CU = *DWCU;
+    if (LocListWritersByCU.count(CU.getOffset()))
+      continue;
+
     const uint16_t DwarfVersion = CU.getVersion();
     if (DwarfVersion >= 5) {
       auto AddrW = std::make_unique<DebugAddrWriterDwarf5>(
           &BC, CU.getAddressByteSize(), CU.getAddrOffsetSectionBase());
       RangeListsSectionWriter->setAddressWriter(AddrW.get());
-      LocListWritersByCU[CUIndex] =
+      LocListWritersByCU[CU.getOffset()] =
           std::make_unique<DebugLoclistWriter>(CU, DwarfVersion, false, *AddrW);
-
       if (std::optional<uint64_t> DWOId = CU.getDWOId()) {
         assert(RangeListsWritersByCU.count(*DWOId) == 0 &&
                "RangeLists writer for DWO unit already exists.");
@@ -663,7 +869,7 @@ void DWARFRewriter::updateDebugInfo() {
       auto AddrW =
           std::make_unique<DebugAddrWriter>(&BC, CU.getAddressByteSize());
       AddressWritersByCU[CU.getOffset()] = std::move(AddrW);
-      LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>();
+      LocListWritersByCU[CU.getOffset()] = std::make_unique<DebugLocWriter>();
       if (std::optional<uint64_t> DWOId = CU.getDWOId()) {
         assert(LegacyRangesWritersByCU.count(*DWOId) == 0 &&
                "LegacyRangeLists writer for DWO unit already exists.");
@@ -674,86 +880,161 @@ void DWARFRewriter::updateDebugInfo() {
             std::move(LegacyRangesSectionWriterByCU);
       }
     }
-    LocListWritersIndexByCU[CU.getOffset()] = CUIndex++;
-  };
+  }
+}
 
-  DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
-                                         *StrWriter);
-  if (DebugNamesTable.isCreated())
-    DebugNamesTable.preAllocateUnits(*BC.DwCtx);
-  std::unordered_map<uint64_t, std::string> DWOToNameMap =
-      getDWONameMap(*BC.DwCtx);
-  GDBIndex GDBIndexSection(BC);
-  if (!opts::DwarfOutputPath.empty()) {
-    if (std::error_code EC =
-            sys::fs::create_directories(opts::DwarfOutputPath)) {
-      BC.errs() << "BOLT-ERROR: could not create directory '"
-                << opts::DwarfOutputPath << "': " << EC.message() << '\n';
-      return;
-    }
+void DWARFRewriter::processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder,
+                                        BucketLocalWriter &LocalWriter) {
+  std::optional<DWARFUnit *> SplitCU;
+  std::optional<uint64_t> RangesBase;
+  std::optional<uint64_t> DWOId = Unit.getDWOId();
+  uint8_t DWARFVersion = Unit.getVersion();
+  if (DWOId)
+    SplitCU = BC.getDWOCU(*DWOId);
+  auto LocIt = LocListWritersByCU.find(Unit.getOffset());
+  assert(LocIt != LocListWritersByCU.end() &&
+         "LocListWriter does not exist for CU");
+  DebugLocWriter &DebugLocWriter = *LocIt->second.get();
+  auto AddrIt = AddressWritersByCU.find(Unit.getOffset());
+  assert(AddrIt != AddressWritersByCU.end() &&
+         "AddressWriter does not exist for CU");
+  DebugAddrWriter &AddressWriter = *AddrIt->second.get();
+  if (DWARFVersion >= 5) {
+    if (!LocalWriter.RngListsWriter)
+      LocalWriter.RngListsWriter =
+          std::make_unique<DebugRangeListsSectionWriter>();
+  } else {
+    if (!LocalWriter.LegacyRangesWriter)
+      LocalWriter.LegacyRangesWriter =
+          std::make_unique<DebugRangesSectionWriter>();
+  }
+
+  DebugRangesSectionWriter &RangesSectionWriter =
+      DWARFVersion >= 5 ? *LocalWriter.RngListsWriter
+                        : *LocalWriter.LegacyRangesWriter;
+
+  if (DWARFVersion >= 5) {
+    LocalWriter.RngListsWriter->setAddressWriter(&AddressWriter);
+    RangesBase = RangesSectionWriter.getSectionOffset() +
+                 getDWARF5RngListLocListHeaderSize();
+    RangesSectionWriter.initSection(Unit);
+  } else if (SplitCU) {
+    // DWARF4: only split-dwarf CUs need a ranges base
+    RangesBase = LocalWriter.LegacyRangesWriter->getSectionOffset();
   }
-  auto processSplitCU = [&](DWARFUnit &Unit, DWARFUnit &SplitCU,
+  updateUnitDebugInfo(Unit, DIEBlder, DebugLocWriter, RangesSectionWriter,
+                      AddressWriter, RangesBase);
+  DebugLocWriter.finalize(DIEBlder, *DIEBlder.getUnitDIEbyUnit(Unit));
+  if (DWARFVersion >= 5)
+    RangesSectionWriter.finalizeSection();
+}
+
+void DWARFRewriter::processBucket(
+    size_t Idx, std::vector<std::vector<DWARFUnit *>> &PartVec,
+    std::vector<std::unique_ptr<DIEBuilder>> &BucketDIEBlders,
+    std::vector<BucketLocalWriter> &LocalWriters,
+    DWARF5AcceleratorTable &DebugNamesTable,
+    std::unordered_map<uint64_t, std::string> &DWOToNameMap,
+    GDBIndex &GDBIndexSection) {
+  std::vector<DWARFUnit *> &Vec = PartVec[Idx];
+  std::unique_ptr<DIEBuilder> &BucketDIEBlder = BucketDIEBlders[Idx];
+  BucketDIEBlder =
+      std::make_unique<DIEBuilder>(BC, BC.DwCtx.get(), DebugNamesTable);
+  DIEBuilder &PartDIEBlder = *BucketDIEBlder;
+  PartDIEBlder.buildCompileUnits(Vec);
+  auto ProcessSplitCU = [&](DWARFUnit &Unit, DWARFUnit &SplitCU,
                             DebugRangesSectionWriter &TempRangesSectionWriter,
                             DebugAddrWriter &AddressWriter,
-                            const std::string DWOName,
+                            const std::string &DWOName,
                             DIEBuilder &DWODIEBuilder) {
     DWODIEBuilder.buildDWOUnit(SplitCU);
     DebugStrOffsetsWriter DWOStrOffstsWriter(BC);
-    DebugStrWriter DWOStrWriter((SplitCU).getContext(), true);
+    DebugStrWriter DWOStrWriter(SplitCU.getContext(), true);
     DWODIEBuilder.updateDWONameCompDirForTypes(DWOStrOffstsWriter, DWOStrWriter,
                                                SplitCU, opts::DwarfOutputPath,
                                                DWOName);
     DebugLoclistWriter DebugLocDWoWriter(Unit, Unit.getVersion(), true,
                                          AddressWriter);
-
     updateUnitDebugInfo(SplitCU, DWODIEBuilder, DebugLocDWoWriter,
                         TempRangesSectionWriter, AddressWriter);
-    DIE *UnitDIE = DWODIEBuilder.getUnitDIEbyUnit(SplitCU);
-    if (!UnitDIE) {
-      errs() << "BOLT-WARNING: failed to construct DIE for split CU "
-             << Twine::utohexstr(*Unit.getDWOId()) << "\n";
-      return;
-    }
-    DebugLocDWoWriter.finalize(DWODIEBuilder, *UnitDIE);
+    DebugLocDWoWriter.finalize(DWODIEBuilder,
+                               *DWODIEBuilder.getUnitDIEbyUnit(SplitCU));
     if (Unit.getVersion() >= 5)
       TempRangesSectionWriter.finalizeSection();
-
     emitDWOBuilder(DWOName, DWODIEBuilder, *this, SplitCU, Unit,
                    DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter,
                    GDBIndexSection, TempRangesSectionWriter);
+    {
+      std::lock_guard<std::mutex> Lock(DebugNamesUpdateMutex);
+      DWODIEBuilder.updateDebugNamesTable();
+    }
   };
-  auto processMainBinaryCU = [&](DWARFUnit &Unit, DIEBuilder &DIEBlder) {
+
+  for (DWARFUnit *CU : PartDIEBlder.getProcessedCUs()) {
     std::optional<DWARFUnit *> SplitCU;
-    std::optional<uint64_t> RangesBase;
-    std::optional<uint64_t> DWOId = Unit.getDWOId();
+    std::optional<uint64_t> DWOId = CU->getDWOId();
     if (DWOId)
       SplitCU = BC.getDWOCU(*DWOId);
-    DebugLocWriter &DebugLocWriter =
-        *LocListWritersByCU[LocListWritersIndexByCU[Unit.getOffset()]].get();
-    DebugRangesSectionWriter &RangesSectionWriter =
-        Unit.getVersion() >= 5 ? *RangeListsSectionWriter
-                               : *LegacyRangesSectionWriter;
-    DebugAddrWriter &AddressWriter =
-        *AddressWritersByCU[Unit.getOffset()].get();
-    if (Unit.getVersion() >= 5)
-      RangeListsSectionWriter->setAddressWriter(&AddressWriter);
-    if (Unit.getVersion() >= 5) {
-      RangesBase = RangesSectionWriter.getSectionOffset() +
-                   getDWARF5RngListLocListHeaderSize();
-      RangesSectionWriter.initSection(Unit);
-      if (!SplitCU)
-        StrOffstsWriter->finalizeSection(Unit, DIEBlder);
-    } else if (SplitCU) {
-      RangesBase = LegacyRangesSectionWriter->getSectionOffset();
+    if (SplitCU) {
+      DebugAddrWriter &AddressWriter = *AddressWritersByCU.at(CU->getOffset());
+      DebugRangesSectionWriter &TempRangesSectionWriter =
+          CU->getVersion() >= 5 ? *RangeListsWritersByCU.at(*DWOId)
+                                : *LegacyRangesWritersByCU.at(*DWOId);
+      auto NameIt = DWOToNameMap.find(*DWOId);
+      if (NameIt == DWOToNameMap.end()) {
+        BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
+                     "DWO name for DWO ID "
+                  << Twine::utohexstr(*DWOId) << ".\n";
+        continue;
+      }
+      auto DWOName = NameIt->second;
+      auto DWODIEBuilderPtr = std::make_unique<DIEBuilder>(
+          BC, &(**SplitCU).getContext(), DebugNamesTable, CU);
+      DIEBuilder &DWODIEBuilder = *DWODIEBuilderPtr;
+      ProcessSplitCU(*CU, **SplitCU, TempRangesSectionWriter, AddressWriter,
+                     DWOName, DWODIEBuilder);
     }
+    processMainBinaryCU(*CU, PartDIEBlder, LocalWriters[Idx]);
+  }
+}
 
-    updateUnitDebugInfo(Unit, DIEBlder, DebugLocWriter, RangesSectionWriter,
-                        AddressWriter, RangesBase);
-    DebugLocWriter.finalize(DIEBlder, *DIEBlder.getUnitDIEbyUnit(Unit));
-    if (Unit.getVersion() >= 5)
-      RangesSectionWriter.finalizeSection();
-  };
+void DWARFRewriter::updateDebugInfo() {
+  ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info");
+  if (!DebugInfo)
+    return;
+  ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
+  StrWriter = std::make_unique<DebugStrWriter>(*BC.DwCtx, false);
+  StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(BC);
+  /// Stores and serializes information that will be put into the
+  /// .debug_addr DWARF section.
+  std::unique_ptr<DebugAddrWriter> FinalAddrWriter;
+  if (BC.isDWARF5Used()) {
+    FinalAddrWriter = std::make_unique<DebugAddrWriterDwarf5>(&BC);
+    RangeListsSectionWriter = std::make_unique<DebugRangeListsSectionWriter>();
+  } else {
+    FinalAddrWriter = std::make_unique<DebugAddrWriter>(&BC);
+  }
+  if (BC.isDWARFLegacyUsed()) {
+    LegacyRangesSectionWriter = std::make_unique<DebugRangesSectionWriter>();
+    LegacyRangesSectionWriter->initSection();
+  }
 
+  uint32_t CUSize = std::distance(BC.DwCtx->compile_units().begin(),
+                                  BC.DwCtx->compile_units().end());
+  // Pre-create per-CU writers in a single thread.
+  // Worker threads should only read from these maps.
+  createRangeLocListAndAddressWriters();
+  // If the user requested an output directory for rewritten .dwo files, make
+  // sure it exists before any worker attempt to create the files.
+  if (!opts::DwarfOutputPath.empty() && !sys::fs::exists(opts::DwarfOutputPath))
+    (void)sys::fs::create_directories(opts::DwarfOutputPath);
+  std::unordered_map<uint64_t, std::string> DWOToNameMap =
+      getDWONameMap(*BC.DwCtx);
+  DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
+                                         *StrWriter);
+  if (DebugNamesTable.isCreated())
+    DebugNamesTable.preAllocateUnits(*BC.DwCtx);
+  GDBIndex GDBIndexSection(BC);
   DIEBuilder DIEBlder(BC, BC.DwCtx.get(), DebugNamesTable);
   DIEBlder.buildTypeUnits(StrOffstsWriter.get());
   SmallVector<char, 20> OutBuffer;
@@ -765,68 +1046,71 @@ void DWARFRewriter::updateDebugInfo() {
       *TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);
   CUOffsetMap OffsetMap =
       finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);
-
-  CUPartitionVector PartVec = partitionCUs(*BC.DwCtx);
+  std::vector<std::vector<DWARFUnit *>> PartVec = partitionCUs(*BC.DwCtx);
   const unsigned int ThreadCount =
       std::min(opts::DebugThreadCount, opts::ThreadCount);
-  for (std::vector<DWARFUnit *> &Vec : PartVec) {
-    DIEBlder.buildCompileUnits(Vec);
-    llvm::SmallVector<std::unique_ptr<DIEBuilder>, 72> DWODIEBuildersByCU;
-    ThreadPoolInterface &ThreadPool =
-        ParallelUtilities::getThreadPool(ThreadCount);
-    for (DWARFUnit *CU : DIEBlder.getProcessedCUs()) {
-      createRangeLocListAddressWriters(*CU);
-      std::optional<DWARFUnit *> SplitCU;
-      std::optional<uint64_t> DWOId = CU->getDWOId();
-      if (DWOId)
-        SplitCU = BC.getDWOCU(*DWOId);
-      if (!SplitCU)
-        continue;
-      DebugAddrWriter &AddressWriter =
-          *AddressWritersByCU[CU->getOffset()].get();
-      DebugRangesSectionWriter &TempRangesSectionWriter =
-          CU->getVersion() >= 5 ? *RangeListsWritersByCU[*DWOId].get()
-                                : *LegacyRangesWritersByCU[*DWOId].get();
-      auto NameIt = DWOToNameMap.find(*DWOId);
-      if (NameIt == DWOToNameMap.end()) {
-        BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
-                     "DWO name for DWO ID "
-                  << Twine::utohexstr(*DWOId) << ".\n";
-        continue;
+  llvm::ThreadPoolInterface &ThreadPool =
+      getOrCreateDebugInfoThreadPool(ThreadCount);
+  std::vector<BucketLocalWriter> LocalWriters(PartVec.size());
+  std::vector<std::unique_ptr<DIEBuilder>> BucketDIEBlders(PartVec.size());
+  std::mutex MergeQueueMutex;
+  std::condition_variable MergeQueueCV;
+  std::queue<size_t> MergeQueue;
+  const size_t TotalTasks = PartVec.size();
+  std::vector<char> BucketDone(TotalTasks, 0);
+
+  BucketLocAccumOffset Accum;
+  auto MergeBucket = [&](size_t Idx) -> void {
+    std::unique_ptr<DIEBuilder> &BucketDIEBlder = BucketDIEBlders[Idx];
+    assert(BucketDIEBlder && "Bucket DIEBuilder is null.");
+
+    mergePerBucketLocsAndRanges(*BucketDIEBlder, LocalWriters[Idx],
+                                DWOToNameMap, Accum);
+    finalizeCompileUnits(*BucketDIEBlder, DIEBlder, *Streamer, OffsetMap,
+                         BucketDIEBlder->getProcessedCUs(), *FinalAddrWriter);
+    BucketDIEBlders[Idx].reset();
+    LocalWriters[Idx].clear();
+  };
+
+  for (size_t I = 0; I < TotalTasks; ++I) {
+    ThreadPool.async([&, I] {
+      processBucket(I, PartVec, BucketDIEBlders, LocalWriters, DebugNamesTable,
+                    DWOToNameMap, GDBIndexSection);
+      {
+        std::lock_guard<std::mutex> Lock(MergeQueueMutex);
+        BucketDone[I] = 1;
+        if (!opts::PreserveOrder)
+          MergeQueue.push(I);
+      }
+      // Only the merge thread waits on the CV and it waits on a specific
+      // index. notify_one is sufficient and avoids waking all workers.
+      MergeQueueCV.notify_one();
+    });
+  }
+
+  // Merge completed buckets in-order or as they finish, depending on
+  // PreserveOrder.
+  for (size_t Idx = 0; Idx < TotalTasks; ++Idx) {
+    size_t ProcessIndex;
+    {
+      std::unique_lock<std::mutex> Lock(MergeQueueMutex);
+      if (opts::PreserveOrder) {
+        MergeQueueCV.wait(Lock, [&] { return BucketDone[Idx] != 0; });
+        ProcessIndex = Idx;
+      } else {
+        MergeQueueCV.wait(Lock, [&] { return !MergeQueue.empty(); });
+        ProcessIndex = MergeQueue.front();
+        MergeQueue.pop();
       }
-      auto DWOName = NameIt->second;
-      DIEBlder.updateDWONameCompDir(*StrOffstsWriter, *StrWriter, *CU,
-                                    opts::DwarfOutputPath, DWOName);
-      auto DWODIEBuilderPtr = std::make_unique<DIEBuilder>(
-          BC, &(**SplitCU).getContext(), DebugNamesTable, CU);
-      DIEBuilder &DWODIEBuilder =
-          *DWODIEBuildersByCU.emplace_back(std::move(DWODIEBuilderPtr));
-      if (CU->getVersion() >= 5)
-        StrOffstsWriter->finalizeSection(*CU, DIEBlder);
-      // Important to capture CU and SplitCU by value here, otherwise when the
-      // thread is executed at some point after the current iteration of the
-      // loop, dereferencing CU/SplitCU in the call to processSplitCU means it
-      // will dereference a different variable than the one intended, causing a
-      // seg fault.
-      ThreadPool.async([&, DWOName, CU, SplitCU] {
-        processSplitCU(*CU, **SplitCU, TempRangesSectionWriter, AddressWriter,
-                       DWOName, DWODIEBuilder);
-      });
     }
-    ThreadPool.wait();
-    for (std::unique_ptr<DIEBuilder> &DWODIEBuilderPtr : DWODIEBuildersByCU)
-      DWODIEBuilderPtr->updateDebugNamesTable();
-    for (DWARFUnit *CU : DIEBlder.getProcessedCUs())
-      processMainBinaryCU(*CU, DIEBlder);
-    finalizeCompileUnits(DIEBlder, *Streamer, OffsetMap,
-                         DIEBlder.getProcessedCUs(), *FinalAddrWriter);
+    MergeBucket(ProcessIndex);
   }
 
+  ThreadPool.wait();
   DebugNamesTable.emitAccelTable();
-
   finalizeDebugSections(DIEBlder, DebugNamesTable, *Streamer, *ObjOS, OffsetMap,
-                        *FinalAddrWriter);
-  GDBIndexSection.updateGdbIndexSection(OffsetMap, CUIndex,
+                        *FinalAddrWriter, Accum.LocListCUOrder);
+  GDBIndexSection.updateGdbIndexSection(OffsetMap, CUSize,
                                         *ARangesSectionWriter);
 }
 
@@ -916,7 +1200,7 @@ void DWARFRewriter::updateUnitDebugInfo(
         ARangesSectionWriter->addCURanges(Unit.getOffset(),
                                           std::move(OutputRanges));
       updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset,
-                                     RangesBase);
+                                     AddressWriter, RangesBase);
       DIEValue StmtListAttrVal = Die->findAttribute(dwarf::DW_AT_stmt_list);
       if (LineTablePatchMap.count(&Unit))
         DIEBldr.replaceValue(Die, dwarf::DW_AT_stmt_list,
@@ -971,7 +1255,8 @@ void DWARFRewriter::updateUnitDebugInfo(
       }
 
       updateDWARFObjectAddressRanges(
-          Unit, DIEBldr, *Die, RangesSectionWriter.addRanges(FunctionRanges));
+          Unit, DIEBldr, *Die, RangesSectionWriter.addRanges(FunctionRanges),
+          AddressWriter);
 
       break;
     }
@@ -1016,7 +1301,8 @@ void DWARFRewriter::updateUnitDebugInfo(
                           OutputRanges.back().HighPC);
         break;
       }
-      updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset);
+      updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset,
+                                     AddressWriter);
       break;
     }
     case dwarf::DW_TAG_call_site: {
@@ -1294,7 +1580,6 @@ void DWARFRewriter::updateUnitDebugInfo(
           dwarf::Form Form = LowPCAttrInfo.getForm();
           assert(Form != dwarf::DW_FORM_LLVM_addrx_offset &&
                  "DW_FORM_LLVM_addrx_offset is not supported");
-          std::lock_guard<std::mutex> Lock(DWARFRewriterMutex);
           if (Form == dwarf::DW_FORM_addrx ||
               Form == dwarf::DW_FORM_GNU_addr_index) {
             const uint32_t Index = AddressWriter.getIndexFromAddress(
@@ -1321,7 +1606,7 @@ void DWARFRewriter::updateUnitDebugInfo(
 
 void DWARFRewriter::updateDWARFObjectAddressRanges(
     DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, uint64_t DebugRangesOffset,
-    std::optional<uint64_t> RangesBase) {
+    DebugAddrWriter &AddressWriter, std::optional<uint64_t> RangesBase) {
 
   if (RangesBase) {
     // If DW_AT_GNU_ranges_base is present, update it. No further modifications
@@ -1402,9 +1687,9 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
   // to back. Replace with new attributes and patch the DIE.
   DIEValue HighPCAttrInfo = Die.findAttribute(dwarf::DW_AT_high_pc);
   if (LowPCAttrInfo && HighPCAttrInfo) {
-
     convertToRangesPatchDebugInfo(Unit, DIEBldr, Die, DebugRangesOffset,
-                                  LowPCAttrInfo, HighPCAttrInfo, RangesBase);
+                                  LowPCAttrInfo, HighPCAttrInfo, AddressWriter,
+                                  RangesBase);
   } else if (!(Unit.isDWOUnit() &&
                Die.getTag() == dwarf::DW_TAG_compile_unit)) {
     if (opts::Verbosity >= 1)
@@ -1454,8 +1739,7 @@ void DWARFRewriter::updateLineTableOffsets(const MCAssembler &Asm) {
     if (!StmtOffset)
       continue;
 
-    const uint64_t LineTableOffset =
-        Asm.getSymbolOffset(*Label);
+    const uint64_t LineTableOffset = Asm.getSymbolOffset(*Label);
     DebugLineOffsetMap[*StmtOffset] = LineTableOffset;
     assert(DbgInfoSection && ".debug_info section must exist");
     LineTablePatchMap[CU.get()] = LineTableOffset;
@@ -1562,7 +1846,7 @@ CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
 void DWARFRewriter::finalizeDebugSections(
     DIEBuilder &DIEBlder, DWARF5AcceleratorTable &DebugNamesTable,
     DIEStreamer &Streamer, raw_svector_ostream &ObjOS, CUOffsetMap &CUMap,
-    DebugAddrWriter &FinalAddrWriter) {
+    DebugAddrWriter &FinalAddrWriter, std::vector<uint64_t> SortedCU) {
   if (StrWriter->isInitialized()) {
     RewriteInstance::addToDebugSectionsToOverwrite(".debug_str");
     std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents =
@@ -1599,7 +1883,7 @@ void DWARFRewriter::finalizeDebugSections(
 
   if (BC.isDWARF5Used()) {
     std::unique_ptr<DebugBufferVector> LocationListSectionContents =
-        makeFinalLocListsSection(DWARFVersion::DWARF5);
+        makeFinalLocListsSection(DWARFVersion::DWARF5, SortedCU);
     if (!LocationListSectionContents->empty())
       BC.registerOrUpdateNoteSection(
           ".debug_loclists", copyByteArray(*LocationListSectionContents),
@@ -1608,7 +1892,7 @@ void DWARFRewriter::finalizeDebugSections(
 
   if (BC.isDWARFLegacyUsed()) {
     std::unique_ptr<DebugBufferVector> LocationListSectionContents =
-        makeFinalLocListsSection(DWARFVersion::DWARFLegacy);
+        makeFinalLocListsSection(DWARFVersion::DWARFLegacy, SortedCU);
     if (!LocationListSectionContents->empty())
       BC.registerOrUpdateNoteSection(
           ".debug_loc", copyByteArray(*LocationListSectionContents),
@@ -1669,8 +1953,8 @@ void DWARFRewriter::finalizeDebugSections(
                                    DebugNamesSectionContents->size());
   }
 }
-
 void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder,
+                                         DIEBuilder &MainDIEBuilder,
                                          DIEStreamer &Streamer,
                                          CUOffsetMap &CUMap,
                                          const std::list<DWARFUnit *> &CUs,
@@ -1715,9 +1999,17 @@ void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder,
         LegacyRangesWriter->releaseBuffer();
     LegacyRangesSectionWriter->appendToRangeBuffer(*RangesWritersContents);
   }
-  DIEBlder.generateAbbrevs();
+  for (DWARFUnit *DU : CUs) {
+    DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*DU);
+    MainDIEBuilder.generateUnitAbbrevs(UnitDIE);
+  }
+  DIEBlder.syncAbbrevTableFrom(MainDIEBuilder);
+  DIEBlder.setUnitOffsetBases(CUOffset);
   DIEBlder.finish();
-  DIEBlder.updateDebugNamesTable();
+  {
+    std::lock_guard<std::mutex> Lock(DebugNamesUpdateMutex);
+    DIEBlder.updateDebugNamesTable();
+  }
   // generate debug_info and CUMap
   for (DWARFUnit *CU : CUs) {
     emitUnit(DIEBlder, Streamer, *CU);
@@ -2073,16 +2365,24 @@ void DWARFRewriter::writeDWOFiles(
 }
 
 std::unique_ptr<DebugBufferVector>
-DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version) {
+DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version,
+                                        ArrayRef<uint64_t> CUOrder) {
   auto LocBuffer = std::make_unique<DebugBufferVector>();
   auto LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer);
   auto Writer =
       std::unique_ptr<MCObjectWriter>(BC.createObjectWriter(*LocStream));
 
-  for (std::pair<const uint64_t, std::unique_ptr<DebugLocWriter>> &Loc :
-       LocListWritersByCU) {
-    DebugLocWriter *LocWriter = Loc.second.get();
-    auto *LocListWriter = llvm::dyn_cast<DebugLoclistWriter>(LocWriter);
+  // For DWARF4 .debug_loc, reserve the first 16 bytes for the empty list.
+  if (Version == DWARFVersion::DWARFLegacy)
+    LocStream->write_zeros(16);
+
+  for (uint64_t CUOffset : CUOrder) {
+    auto It = LocListWritersByCU.find(CUOffset);
+    if (It == LocListWritersByCU.end())
+      continue;
+
+    DebugLocWriter *LocWriter = It->second.get();
+    auto *LocListWriter = dyn_cast<DebugLoclistWriter>(LocWriter);
 
     // Filter out DWARF4, writing out DWARF5
     if (Version == DWARFVersion::DWARF5 &&
@@ -2097,8 +2397,19 @@ DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version) {
     // Skipping DWARF4/5 split dwarf.
     if (LocListWriter && LocListWriter->getDwarfVersion() <= 4)
       continue;
+
     std::unique_ptr<DebugBufferVector> CurrCULocationLists =
         LocWriter->getBuffer();
+    // For DWARF4, each per-CU buffer begins with a 16-byte empty list.
+    // Emit the empty list only once globally (reserved above)
+    if (Version == DWARFVersion::DWARFLegacy) {
+      if (CurrCULocationLists->size() > 16)
+        *LocStream << StringRef(
+            reinterpret_cast<const char *>(CurrCULocationLists->data() + 16),
+            CurrCULocationLists->size() - 16);
+      continue;
+    }
+
     *LocStream << *CurrCULocationLists;
   }
 
@@ -2108,7 +2419,8 @@ DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version) {
 void DWARFRewriter::convertToRangesPatchDebugInfo(
     DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die,
     uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo,
-    DIEValue &HighPCAttrInfo, std::optional<uint64_t> RangesBase) {
+    DIEValue &HighPCAttrInfo, DebugAddrWriter &AddressWriter,
+    std::optional<uint64_t> RangesBase) {
   dwarf::Form LowForm = LowPCAttrInfo.getForm();
   dwarf::Attribute RangeBaseAttribute = dwarf::DW_AT_GNU_ranges_base;
   dwarf::Form RangesForm = dwarf::DW_FORM_sec_offset;
@@ -2129,11 +2441,7 @@ void DWARFRewriter::convertToRangesPatchDebugInfo(
   // when it's absent.
   if (IsUnitDie) {
     if (LowForm == dwarf::DW_FORM_addrx) {
-      auto AddrWriterIterator = AddressWritersByCU.find(Unit.getOffset());
-      assert(AddrWriterIterator != AddressWritersByCU.end() &&
-             "AddressWriter does not exist for CU");
-      DebugAddrWriter *AddrWriter = AddrWriterIterator->second.get();
-      const uint32_t Index = AddrWriter->getIndexFromAddress(0, Unit);
+      const uint32_t Index = AddressWriter.getIndexFromAddress(0, Unit);
       DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(),
                            LowPCAttrInfo.getForm(), DIEInteger(Index));
     } else {
diff --git a/bolt/test/X86/dwarf4-cross-cu-backward-different-abbrev.test b/bolt/test/X86/dwarf4-cross-cu-backward-different-abbrev.test
index b06cec6fe6b96..313ea0150a0a2 100644
--- a/bolt/test/X86/dwarf4-cross-cu-backward-different-abbrev.test
+++ b/bolt/test/X86/dwarf4-cross-cu-backward-different-abbrev.test
@@ -17,9 +17,9 @@
 # PRECHECK: DW_AT_abstract_origin [DW_FORM_ref_addr]
 # PRECHECK-SAME: "var"
 
+# POSTCHECK: DW_TAG_compile_unit
 # POSTCHECK: DW_TAG_compile_unit
 # POSTCHECK: DW_AT_abstract_origin [DW_FORM_ref_addr]
 # POSTCHECK-SAME: "inlined"
 # POSTCHECK: DW_AT_abstract_origin [DW_FORM_ref_addr]
 # POSTCHECK-SAME: "var"
-# POSTCHECK: DW_TAG_compile_unit
diff --git a/bolt/test/X86/dwarf4-cross-cu-ranges.test b/bolt/test/X86/dwarf4-cross-cu-ranges.test
new file mode 100644
index 0000000000000..c1f0c8fcfb9ee
--- /dev/null
+++ b/bolt/test/X86/dwarf4-cross-cu-ranges.test
@@ -0,0 +1,55 @@
+; REQUIRES: system-linux
+
+; RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf4-cross-cu-with-loclist.s -o %t.o
+; RUN: %clang %cflags -nostdlib -no-pie %t.o -o %t.exe
+; RUN: llvm-bolt %t.exe -o %t.exe.bolt --update-debug-sections --always-convert-to-ranges --debug-thread-count=4
+; RUN: llvm-objdump --disassemble %t.exe.bolt > %t.txt
+; RUN: llvm-dwarfdump --show-form --verbose --debug-info --debug-ranges %t.exe.bolt >> %t.txt
+; RUN: FileCheck %s --input-file=%t.txt
+
+;; Test that two cross-referenced CUs processed in the same bucket keep their own .debug_ranges entries.
+
+; CHECK: <main>:
+; CHECK-NEXT: [[#%.6x,MAIN:]]
+; CHECK: <_Z4foo2i>:
+; CHECK-NEXT: [[#%.6x,FOO2:]]
+
+; CHECK:      DW_AT_name [DW_FORM_strp]{{.*}}= "main.cpp"
+; CHECK-NEXT: DW_AT_stmt_list [DW_FORM_sec_offset]
+; CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strp]
+; CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
+; CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x[[#%.8x,MAIN_CU_RANGES:]]
+; CHECK-NEXT: [0x0000000000[[#%.6x,MAIN]], 0x0000000000[[#%.6x,MAIN_END:]]))
+
+; CHECK:      DW_TAG_subprogram [2]
+; CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x[[#%.8x,MAIN_SUB_RANGES:]]
+; CHECK-NEXT: [0x0000000000[[#MAIN]], 0x0000000000[[#MAIN_END]]))
+; CHECK-NEXT: DW_AT_frame_base [DW_FORM_exprloc]
+; CHECK-NEXT: DW_AT_GNU_all_call_sites [DW_FORM_flag_present]
+; CHECK-NEXT: DW_AT_name [DW_FORM_strp]{{.*}}= "main"
+
+; CHECK:      DW_AT_name [DW_FORM_strp]{{.*}}= "helper1.cpp"
+; CHECK-NEXT: DW_AT_stmt_list [DW_FORM_sec_offset]
+; CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strp]
+; CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
+; CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x[[#%.8x,HELPER_CU_RANGES:]]
+; CHECK-NEXT: [0x0000000000[[#FOO2]], 0x0000000000[[#%.6x,FOO2_END:]]))
+
+; CHECK:      DW_TAG_subprogram [14]
+; CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x[[#%.8x,HELPER_SUB_RANGES:]]
+; CHECK-NEXT: [0x0000000000[[#FOO2]], 0x0000000000[[#FOO2_END]]))
+; CHECK-NEXT: DW_AT_frame_base [DW_FORM_exprloc]
+; CHECK-NEXT: DW_AT_GNU_all_call_sites [DW_FORM_flag_present]
+; CHECK-NEXT: DW_AT_linkage_name [DW_FORM_strp]{{.*}}= "_Z4foo2i"
+; CHECK-NEXT: DW_AT_name [DW_FORM_strp]{{.*}}= "foo2"
+
+; CHECK:      DW_TAG_inlined_subroutine
+; CHECK-NEXT: DW_AT_abstract_origin
+; CHECK-NEXT: DW_AT_ranges {{.*}}(0x[[#%.8x,INLINED_RANGES:]]
+
+; CHECK:      .debug_ranges contents:
+; CHECK:      [[#%.8x,MAIN_CU_RANGES]] 0000000000[[#%.6x,MAIN]] 0000000000[[#%.6x,MAIN + 0x8]]
+; CHECK:      [[#%.8x,MAIN_SUB_RANGES]] 0000000000[[#%.6x,MAIN]] 0000000000[[#%.6x,MAIN + 0x8]]
+; CHECK:      [[#%.8x,HELPER_CU_RANGES]] 0000000000[[#%.6x,FOO2]] 0000000000[[#%.6x,FOO2 + 0x4]]
+; CHECK:      [[#%.8x,HELPER_SUB_RANGES]] 0000000000[[#%.6x,FOO2]] 0000000000[[#%.6x,FOO2 + 0x4]]
+; CHECK:      [[#%.8x,INLINED_RANGES]] 0000000000[[#%.6x,FOO2]] 0000000000[[#%.6x,FOO2 + 0x3]]
\ No newline at end of file

>From 8ec10742354f63b39c1d2aa9ff62634383938c56 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Thu, 28 May 2026 12:42:27 +0800
Subject: [PATCH 02/10] fix some bugs

---
 bolt/include/bolt/Core/DebugData.h |  2 +-
 bolt/lib/Rewrite/DWARFRewriter.cpp | 28 ++++++++++++++++++++--------
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index f1120c17fee9f..67a1c5c739c25 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -175,7 +175,7 @@ class DebugRangesSectionWriter {
 
   /// Returns an offset of an empty address ranges list that is always written
   /// to .debug_ranges
-  uint64_t getEmptyRangesOffset() const { return EmptyRangesOffset; }
+  static uint64_t getEmptyRangesOffset() { return EmptyRangesOffset; }
 
   /// Returns the SectionOffset.
   uint64_t getSectionOffset();
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 1b8327b624444..ba9e59acb54f0 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -354,7 +354,7 @@ static cl::opt<unsigned>
     DebugThreadCount("debug-thread-count",
                      cl::desc("specifies thread count for the multithreading "
                               "for updating DWARF debug info"),
-                     cl::init(1), cl::cat(BoltCategory));
+                     cl::init(32), cl::cat(BoltCategory));
 
 static cl::opt<bool>
     PreserveOrder("dwarf-preserve-order",
@@ -478,8 +478,11 @@ static void fixupDWARFRanges(DIEBuilder &DIEBlder, DWARFUnit &Unit,
     if (RangesVal &&
         (Version < 5 || RangesVal.getForm() == dwarf::DW_FORM_sec_offset)) {
       uint64_t OldVal = RangesVal.getDIEInteger().getValue();
-      DIEBlder.replaceValue(Die, dwarf::DW_AT_ranges, RangesVal.getForm(),
-                            DIEInteger(OldVal + Offset));
+      // Empty ranges are stored at offset 0 in both local and global
+      // buffers, so must not be shifted during merge.
+      if (OldVal != DebugRangesSectionWriter::getEmptyRangesOffset())
+        DIEBlder.replaceValue(Die, dwarf::DW_AT_ranges, RangesVal.getForm(),
+                              DIEInteger(OldVal + Offset));
     }
 
     if (Version < 5) {
@@ -834,12 +837,19 @@ void DWARFRewriter::mergePerBucketLocsAndRanges(
 
   std::unique_ptr<DebugBufferVector> LegacyBuf =
       LocalWriters.LegacyRangesWriter->releaseBuffer();
-  // emission order is established later by emitBucketCompileUnits
+  // Local buffers reserve offset 0 for an empty ranges terminator via
+  // initSection(). When appending to the global buffer, skip that local
+  // terminator since the global buffer has its own at offset 0. Adjust
+  // the fixup offset to account for the skipped prefix.
+  const uint64_t LocalTerminatorSize = 16;
+  const uint64_t GlobalOffset =
+      LegacyRangesSectionWriter->getSectionOffset() - LocalTerminatorSize;
   for (DWARFUnit *CU : SortedCUs)
     if (CU->getVersion() <= 4)
-      fixupDWARFRanges(PartDIEBlder, *CU,
-                       LegacyRangesSectionWriter->getSectionOffset());
-  LegacyRangesSectionWriter->appendToRangeBuffer(*LegacyBuf);
+      fixupDWARFRanges(PartDIEBlder, *CU, GlobalOffset);
+  DebugBufferVector TrimmedBuf(LegacyBuf->begin() + LocalTerminatorSize,
+                               LegacyBuf->end());
+  LegacyRangesSectionWriter->appendToRangeBuffer(TrimmedBuf);
 }
 
 void DWARFRewriter::createRangeLocListAndAddressWriters() {
@@ -904,9 +914,11 @@ void DWARFRewriter::processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder,
       LocalWriter.RngListsWriter =
           std::make_unique<DebugRangeListsSectionWriter>();
   } else {
-    if (!LocalWriter.LegacyRangesWriter)
+    if (!LocalWriter.LegacyRangesWriter) {
       LocalWriter.LegacyRangesWriter =
           std::make_unique<DebugRangesSectionWriter>();
+      LocalWriter.LegacyRangesWriter->initSection();
+    }
   }
 
   DebugRangesSectionWriter &RangesSectionWriter =

>From eade198b17d3af896595f42048cf0b6b42976d58 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Tue, 2 Jun 2026 17:56:10 +0800
Subject: [PATCH 03/10] fix up

---
 bolt/include/bolt/Core/DIEBuilder.h       |  2 +-
 bolt/include/bolt/Rewrite/DWARFRewriter.h |  2 +-
 bolt/lib/Core/DIEBuilder.cpp              |  2 +-
 bolt/lib/Rewrite/DWARFRewriter.cpp        | 73 ++++++++++++++---------
 4 files changed, 48 insertions(+), 31 deletions(-)

diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h
index 72404e6816e11..f5b5f0a401140 100644
--- a/bolt/include/bolt/Core/DIEBuilder.h
+++ b/bolt/include/bolt/Core/DIEBuilder.h
@@ -290,7 +290,7 @@ class DIEBuilder {
   /// Constructs IR for all the CUs.
   void buildCompileUnits(const bool Init = true);
   /// Constructs IR for CUs in a vector.
-  void buildCompileUnits(const std::vector<DWARFUnit *> &CUs);
+  void buildCompileUnits(const SmallVector<DWARFUnit *> &CUs);
   /// Preventing implicit conversions.
   template <class T> void buildCompileUnits(T) = delete;
   /// Builds DWO Unit. For DWARF5 this includes the type units.
diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h
index 87cdca0ab7ad9..c3184e9ebd64d 100644
--- a/bolt/include/bolt/Rewrite/DWARFRewriter.h
+++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h
@@ -180,7 +180,7 @@ class DWARFRewriter {
   void processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder,
                            BucketLocalWriter &LocalWriter);
 
-  void processBucket(size_t Idx, std::vector<std::vector<DWARFUnit *>> &PartVec,
+  void processBucket(size_t Idx, SmallVector<SmallVector<DWARFUnit *>> &PartVec,
                      std::vector<std::unique_ptr<DIEBuilder>> &BucketDIEBlders,
                      std::vector<BucketLocalWriter> &LocalWriters,
                      DWARF5AcceleratorTable &DebugNamesTable,
diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp
index 4a2b668236a9f..f068b2c97ce1e 100644
--- a/bolt/lib/Core/DIEBuilder.cpp
+++ b/bolt/lib/Core/DIEBuilder.cpp
@@ -381,7 +381,7 @@ void DIEBuilder::buildCompileUnits(const bool Init) {
     constructFromUnit(*DU);
   }
 }
-void DIEBuilder::buildCompileUnits(const std::vector<DWARFUnit *> &CUs) {
+void DIEBuilder::buildCompileUnits(const SmallVector<DWARFUnit *> &CUs) {
   BuilderState.reset(new State());
   // Allocating enough for current batch being processed.
   // In real use cases we either processing a batch of CUs with no cross
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index ba9e59acb54f0..a26bc1d3433c7 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -603,7 +603,7 @@ static void emitDWOBuilder(const std::string &DWOName,
                          StrOffstsWriter, StrWriter, TempRangesSectionWriter);
 }
 
-static std::vector<std::vector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
+static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
   SmallVector<DWARFUnit *, 0> AllCUs;
   for (auto &CU : DwCtx.compile_units())
     AllCUs.push_back(CU.get());
@@ -638,39 +638,56 @@ static std::vector<std::vector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
         }
     if (RefAddrAbbrevs.empty())
       continue;
-    // Track CUs involved in cross-CU references via DW_FORM_ref_addr.
-    for (const DWARFDebugInfoEntry &Entry : CU->dies()) {
-      DWARFDie Die(CU, &Entry);
+    // Track CUs involved in cross-CU references via DW_FORM_ref_addr. Use the
+    // low-level extractor instead of DWARFUnit::dies() to avoid materializing
+    // every DIE in the unit.
+    uint64_t DIEOffset = CU->getOffset() + CU->getHeaderSize();
+    const uint64_t NextCUOffset = CU->getNextUnitOffset();
+    DWARFDataExtractor DebugInfoData = CU->getDebugInfoExtractor();
+    DWARFDebugInfoEntry DIEEntry;
+    SmallVector<uint32_t, 8> Parents;
+    Parents.push_back(UINT32_MAX);
+    do {
+      if (!DIEEntry.extractFast(*CU, &DIEOffset, DebugInfoData, NextCUOffset,
+                                Parents.back()))
+        break;
       const DWARFAbbreviationDeclaration *Abbrev =
-          Die.getAbbreviationDeclarationPtr();
-      if (!Abbrev || !RefAddrAbbrevs.count(Abbrev))
-        continue;
-      for (const DWARFAttribute &Attr : Die.attributes()) {
-        if (Attr.Value.getForm() != dwarf::DW_FORM_ref_addr)
-          continue;
-        auto OptRef = Attr.Value.getAsDebugInfoReference();
-        if (!OptRef)
-          continue;
-        DWARFUnit *TargetCU = FindCuForOffset(*OptRef);
-        if (!TargetCU)
-          continue;
-        if (CrossRefSet.insert(CU).second)
-          EC.insert(CU);
-        if (CrossRefSet.insert(TargetCU).second)
-          EC.insert(TargetCU);
-        EC.unionSets(CU, TargetCU);
+          DIEEntry.getAbbreviationDeclarationPtr();
+      if (Abbrev) {
+        if (RefAddrAbbrevs.count(Abbrev)) {
+          DWARFDie Die(CU, &DIEEntry);
+          for (const DWARFAttribute &Attr : Die.attributes()) {
+            if (Attr.Value.getForm() != dwarf::DW_FORM_ref_addr)
+              continue;
+            auto OptRef = Attr.Value.getAsDebugInfoReference();
+            if (!OptRef)
+              continue;
+            DWARFUnit *TargetCU = FindCuForOffset(*OptRef);
+            if (!TargetCU)
+              continue;
+            if (CrossRefSet.insert(CU).second)
+              EC.insert(CU);
+            if (CrossRefSet.insert(TargetCU).second)
+              EC.insert(TargetCU);
+            EC.unionSets(CU, TargetCU);
+          }
+        }
+        if (Abbrev->hasChildren())
+          Parents.push_back(0);
+      } else {
+        Parents.pop_back();
       }
-    }
+    } while (!Parents.empty());
   }
 
-  DenseMap<DWARFUnit *, std::vector<DWARFUnit *>> MembersByLeader;
+  DenseMap<DWARFUnit *, SmallVector<DWARFUnit *>> MembersByLeader;
   for (DWARFUnit *CU : AllCUs) {
     if (!CrossRefSet.count(CU))
       continue;
     MembersByLeader[EC.getLeaderValue(CU)].push_back(CU);
   }
 
-  std::vector<DWARFUnit *> Leaders;
+  SmallVector<DWARFUnit *> Leaders;
   Leaders.reserve(MembersByLeader.size());
   for (auto &[Leader, Members] : MembersByLeader)
     Leaders.push_back(Leader);
@@ -680,7 +697,7 @@ static std::vector<std::vector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
   });
 
   // Emit cross-ref buckets, then singleton non-cross-ref CUs.
-  std::vector<std::vector<DWARFUnit *>> Vec;
+  SmallVector<SmallVector<DWARFUnit *>> Vec;
   for (DWARFUnit *Leader : Leaders)
     Vec.push_back(std::move(MembersByLeader[Leader]));
   for (DWARFUnit *CU : AllCUs) {
@@ -942,13 +959,13 @@ void DWARFRewriter::processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder,
 }
 
 void DWARFRewriter::processBucket(
-    size_t Idx, std::vector<std::vector<DWARFUnit *>> &PartVec,
+    size_t Idx, SmallVector<SmallVector<DWARFUnit *>> &PartVec,
     std::vector<std::unique_ptr<DIEBuilder>> &BucketDIEBlders,
     std::vector<BucketLocalWriter> &LocalWriters,
     DWARF5AcceleratorTable &DebugNamesTable,
     std::unordered_map<uint64_t, std::string> &DWOToNameMap,
     GDBIndex &GDBIndexSection) {
-  std::vector<DWARFUnit *> &Vec = PartVec[Idx];
+  SmallVector<DWARFUnit *> &Vec = PartVec[Idx];
   std::unique_ptr<DIEBuilder> &BucketDIEBlder = BucketDIEBlders[Idx];
   BucketDIEBlder =
       std::make_unique<DIEBuilder>(BC, BC.DwCtx.get(), DebugNamesTable);
@@ -1058,7 +1075,7 @@ void DWARFRewriter::updateDebugInfo() {
       *TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);
   CUOffsetMap OffsetMap =
       finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);
-  std::vector<std::vector<DWARFUnit *>> PartVec = partitionCUs(*BC.DwCtx);
+  SmallVector<SmallVector<DWARFUnit *>> PartVec = partitionCUs(*BC.DwCtx);
   const unsigned int ThreadCount =
       std::min(opts::DebugThreadCount, opts::ThreadCount);
   llvm::ThreadPoolInterface &ThreadPool =

>From c17ba018fe82ea789d802e60d12b1b6b37ffb324 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Tue, 2 Jun 2026 19:36:45 +0800
Subject: [PATCH 04/10] fix

---
 bolt/lib/Rewrite/DWARFRewriter.cpp | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index a26bc1d3433c7..8439a96a76b6b 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -638,18 +638,16 @@ static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
         }
     if (RefAddrAbbrevs.empty())
       continue;
-    // Track CUs involved in cross-CU references via DW_FORM_ref_addr. Use the
-    // low-level extractor instead of DWARFUnit::dies() to avoid materializing
-    // every DIE in the unit.
+    // Track CUs involved in cross-CU references via DW_FORM_ref_addr.
     uint64_t DIEOffset = CU->getOffset() + CU->getHeaderSize();
     const uint64_t NextCUOffset = CU->getNextUnitOffset();
     DWARFDataExtractor DebugInfoData = CU->getDebugInfoExtractor();
     DWARFDebugInfoEntry DIEEntry;
-    SmallVector<uint32_t, 8> Parents;
-    Parents.push_back(UINT32_MAX);
+    SmallVector<uint32_t, 8> ParentIndex;
+    ParentIndex.push_back(UINT32_MAX);
     do {
       if (!DIEEntry.extractFast(*CU, &DIEOffset, DebugInfoData, NextCUOffset,
-                                Parents.back()))
+                                ParentIndex.back()))
         break;
       const DWARFAbbreviationDeclaration *Abbrev =
           DIEEntry.getAbbreviationDeclarationPtr();
@@ -673,11 +671,11 @@ static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
           }
         }
         if (Abbrev->hasChildren())
-          Parents.push_back(0);
+          ParentIndex.push_back(0);
       } else {
-        Parents.pop_back();
+        ParentIndex.pop_back();
       }
-    } while (!Parents.empty());
+    } while (!ParentIndex.empty());
   }
 
   DenseMap<DWARFUnit *, SmallVector<DWARFUnit *>> MembersByLeader;

>From 24dcdc31634e42cd0c012a6121cf295c11cac727 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Thu, 4 Jun 2026 16:25:29 +0800
Subject: [PATCH 05/10] fix up

---
 bolt/include/bolt/Rewrite/DWARFRewriter.h |  1 +
 bolt/lib/Rewrite/DWARFRewriter.cpp        | 16 +++++++++-------
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h
index c3184e9ebd64d..fe2705abfef04 100644
--- a/bolt/include/bolt/Rewrite/DWARFRewriter.h
+++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h
@@ -180,6 +180,7 @@ class DWARFRewriter {
   void processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder,
                            BucketLocalWriter &LocalWriter);
 
+  /// Process all CUs in the given bucket.
   void processBucket(size_t Idx, SmallVector<SmallVector<DWARFUnit *>> &PartVec,
                      std::vector<std::unique_ptr<DIEBuilder>> &BucketDIEBlders,
                      std::vector<BucketLocalWriter> &LocalWriters,
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 8439a96a76b6b..2eb677a242c7c 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -465,7 +465,7 @@ static void fixupDWARFRanges(DIEBuilder &DIEBlder, DWARFUnit &Unit,
     DIE *CUDie = DIEBlder.getUnitDIEbyUnit(Unit);
     DIEValue RngBaseVal = CUDie->findAttribute(dwarf::DW_AT_rnglists_base);
     if (RngBaseVal) {
-      uint64_t OldVal = RngBaseVal.getDIEInteger().getValue();
+      const uint64_t OldVal = RngBaseVal.getDIEInteger().getValue();
       DIEBlder.replaceValue(CUDie, dwarf::DW_AT_rnglists_base,
                             RngBaseVal.getForm(), DIEInteger(OldVal + Offset));
     }
@@ -477,7 +477,7 @@ static void fixupDWARFRanges(DIEBuilder &DIEBlder, DWARFUnit &Unit,
     DIEValue RangesVal = Die->findAttribute(dwarf::DW_AT_ranges);
     if (RangesVal &&
         (Version < 5 || RangesVal.getForm() == dwarf::DW_FORM_sec_offset)) {
-      uint64_t OldVal = RangesVal.getDIEInteger().getValue();
+      const uint64_t OldVal = RangesVal.getDIEInteger().getValue();
       // Empty ranges are stored at offset 0 in both local and global
       // buffers, so must not be shifted during merge.
       if (OldVal != DebugRangesSectionWriter::getEmptyRangesOffset())
@@ -790,7 +790,6 @@ void DWARFRewriter::mergePerBucketLocsAndRanges(
     finalizeSkeletonAndStrSection(PartDIEBlder, *CU, DWOToNameMap);
   }
   for (DWARFUnit *CU : SortedCUs) {
-
     const uint64_t CUOffset = CU->getOffset();
     // Compute this CU's base and adjust loclists / legacy loc fixups inline.
     uint64_t LoclistsBase = 0;
@@ -808,16 +807,19 @@ void DWARFRewriter::mergePerBucketLocsAndRanges(
         Accum.LoclistsOffset += BufferSize;
       } else if (!LocListWriter) {
         LegacyLocBase = Accum.LegacyLocOffset;
-        // DWARF4 .debug_loc buffers carry a trailing 16-byte terminator
-        // which is written only once at the global level; discount it here
-        // so the next CU's base offset is computed correctly.
+        // Each per-CU DWARF4 .debug_loc buffer begins with a 16-byte empty-list
+        // sentinel (offset 0). That sentinel is emitted once for the whole
+        // section in makeFinalLocListsSection, and each CU's leading 16 bytes
+        // are stripped, so a CU contributes BufferSize-16 bytes to the global
+        // section.
+
         if (BufferSize > 16)
           Accum.LegacyLocOffset += BufferSize - 16;
       }
     }
 
     if (CU->getVersion() >= 5) {
-      uint64_t RangsOffset = RangeListsSectionWriter->getSectionOffset();
+      const uint64_t RangsOffset = RangeListsSectionWriter->getSectionOffset();
       fixupDWARFRanges(PartDIEBlder, *CU, RangsOffset);
 
       if (LoclistsBase) {

>From 688c1e9be7abeb0455cf7dd0112eb848c96811c5 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Fri, 5 Jun 2026 11:23:53 +0800
Subject: [PATCH 06/10] fix

---
 bolt/include/bolt/Rewrite/DWARFRewriter.h | 6 ++----
 bolt/lib/Rewrite/DWARFRewriter.cpp        | 8 ++++++--
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h
index fe2705abfef04..aef24e226e74a 100644
--- a/bolt/include/bolt/Rewrite/DWARFRewriter.h
+++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h
@@ -52,10 +52,8 @@ class DWARFRewriter {
 
     /// Release whichever writer was initialized.
     void clear() {
-      if (RngListsWriter)
-        RngListsWriter.reset();
-      if (LegacyRangesWriter)
-        LegacyRangesWriter.reset();
+      RngListsWriter.reset();
+      LegacyRangesWriter.reset();
     }
   };
 
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 2eb677a242c7c..a2bffd05ab319 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -605,10 +605,15 @@ static void emitDWOBuilder(const std::string &DWOName,
 
 static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
   SmallVector<DWARFUnit *, 0> AllCUs;
+  AllCUs.reserve(std::distance(DwCtx.compile_units().begin(),
+                               DwCtx.compile_units().end()));
   for (auto &CU : DwCtx.compile_units())
     AllCUs.push_back(CU.get());
   if (AllCUs.empty())
     return {};
+  llvm::sort(AllCUs, [](const DWARFUnit *A, const DWARFUnit *B) {
+    return A->getOffset() < B->getOffset();
+  });
   auto FindCuForOffset = [&](uint64_t Offset) -> DWARFUnit * {
     auto *It =
         llvm::upper_bound(AllCUs, Offset, [](uint64_t Off, DWARFUnit *U) {
@@ -699,9 +704,8 @@ static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
   for (DWARFUnit *Leader : Leaders)
     Vec.push_back(std::move(MembersByLeader[Leader]));
   for (DWARFUnit *CU : AllCUs) {
-    if (!CrossRefSet.count(CU)) {
+    if (!CrossRefSet.count(CU))
       Vec.push_back({CU});
-    }
   }
 
   return Vec;

>From 2d23260ee198019893295960d263c64143d816e1 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Mon, 8 Jun 2026 11:07:07 +0800
Subject: [PATCH 07/10] fix ranges code duplication

---
 bolt/lib/Rewrite/DWARFRewriter.cpp | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index a2bffd05ab319..29538b7adda5c 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -484,16 +484,6 @@ static void fixupDWARFRanges(DIEBuilder &DIEBlder, DWARFUnit &Unit,
         DIEBlder.replaceValue(Die, dwarf::DW_AT_ranges, RangesVal.getForm(),
                               DIEInteger(OldVal + Offset));
     }
-
-    if (Version < 5) {
-      DIEValue GnuBaseVal = Die->findAttribute(dwarf::DW_AT_GNU_ranges_base);
-      if (GnuBaseVal) {
-        uint64_t OldVal = GnuBaseVal.getDIEInteger().getValue();
-        DIEBlder.replaceValue(Die, dwarf::DW_AT_GNU_ranges_base,
-                              GnuBaseVal.getForm(),
-                              DIEInteger(OldVal + Offset));
-      }
-    }
   }
 }
 

>From 9ee6c08c76dc2ff665d0541e45c0fa26d1df9291 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Tue, 9 Jun 2026 11:32:15 +0800
Subject: [PATCH 08/10] fix syncAbbrevTableFrom duplication

---
 bolt/include/bolt/Core/DIEBuilder.h |  1 -
 bolt/lib/Core/DIEBuilder.cpp        | 19 -------------------
 bolt/lib/Rewrite/DWARFRewriter.cpp  |  3 ++-
 3 files changed, 2 insertions(+), 21 deletions(-)

diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h
index f5b5f0a401140..8fba755e154d5 100644
--- a/bolt/include/bolt/Core/DIEBuilder.h
+++ b/bolt/include/bolt/Core/DIEBuilder.h
@@ -341,7 +341,6 @@ class DIEBuilder {
   void generateAbbrevs();
   void generateUnitAbbrevs(DIE *Die);
   void assignAbbrev(DIEAbbrev &Abbrev);
-  void syncAbbrevTableFrom(const DIEBuilder &SrcBuilder);
   /// Set the base offset for CU emission, used by the incremental merge
   /// pipeline
   void setUnitOffsetBases(uint64_t Base) {
diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp
index f068b2c97ce1e..9ee6059a3cc88 100644
--- a/bolt/lib/Core/DIEBuilder.cpp
+++ b/bolt/lib/Core/DIEBuilder.cpp
@@ -1040,25 +1040,6 @@ void DIEBuilder::generateUnitAbbrevs(DIE *Die) {
   }
 }
 
-void DIEBuilder::syncAbbrevTableFrom(const DIEBuilder &SrcBuilder) {
-  Abbreviations.clear();
-  AbbreviationsSet.clear();
-  Abbreviations.reserve(SrcBuilder.Abbreviations.size());
-  for (const auto &SrcAbbrev : SrcBuilder.Abbreviations) {
-    auto Copy = std::make_unique<DIEAbbrev>(SrcAbbrev->getTag(),
-                                            SrcAbbrev->hasChildren());
-    for (const auto &Attr : SrcAbbrev->getData())
-      Copy->AddAttribute(Attr.getAttribute(), Attr.getForm());
-    Copy->setNumber(SrcAbbrev->getNumber());
-    FoldingSetNodeID ID;
-    Copy->Profile(ID);
-    void *InsertToken;
-    if (!AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken))
-      AbbreviationsSet.InsertNode(Copy.get(), InsertToken);
-    Abbreviations.push_back(std::move(Copy));
-  }
-}
-
 static uint64_t getHash(const DWARFUnit &DU) {
   // Before DWARF5 TU units are in their own section, so at least one offset,
   // first one, will be the same as CUs in .debug_info.dwo section
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 29538b7adda5c..ab15f452d226a 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -2022,11 +2022,12 @@ void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder,
         LegacyRangesWriter->releaseBuffer();
     LegacyRangesSectionWriter->appendToRangeBuffer(*RangesWritersContents);
   }
+  // Assign abbreviation numbers from the main DIEBuilder so all bucket DIEs
+  // share a single global .debug_abbrev table.
   for (DWARFUnit *DU : CUs) {
     DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*DU);
     MainDIEBuilder.generateUnitAbbrevs(UnitDIE);
   }
-  DIEBlder.syncAbbrevTableFrom(MainDIEBuilder);
   DIEBlder.setUnitOffsetBases(CUOffset);
   DIEBlder.finish();
   {

>From 5532df4b2040ab5f3dfecaee96146f32ad5946a8 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Wed, 10 Jun 2026 14:09:48 +0800
Subject: [PATCH 09/10] fix FindCuForOffset() duplication

---
 bolt/include/bolt/Rewrite/DWARFRewriter.h |  6 ----
 bolt/lib/Rewrite/DWARFRewriter.cpp        | 40 ++++++++---------------
 2 files changed, 13 insertions(+), 33 deletions(-)

diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h
index aef24e226e74a..f0f536fc0cbc0 100644
--- a/bolt/include/bolt/Rewrite/DWARFRewriter.h
+++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h
@@ -49,12 +49,6 @@ class DWARFRewriter {
   struct BucketLocalWriter {
     std::unique_ptr<DebugRangeListsSectionWriter> RngListsWriter;
     std::unique_ptr<DebugRangesSectionWriter> LegacyRangesWriter;
-
-    /// Release whichever writer was initialized.
-    void clear() {
-      RngListsWriter.reset();
-      LegacyRangesWriter.reset();
-    }
   };
 
 private:
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index ab15f452d226a..254cb0ab20f9a 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -593,30 +593,14 @@ static void emitDWOBuilder(const std::string &DWOName,
                          StrOffstsWriter, StrWriter, TempRangesSectionWriter);
 }
 
-static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
+static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx,
+                                                          const size_t Size) {
   SmallVector<DWARFUnit *, 0> AllCUs;
-  AllCUs.reserve(std::distance(DwCtx.compile_units().begin(),
-                               DwCtx.compile_units().end()));
+  AllCUs.reserve(Size);
   for (auto &CU : DwCtx.compile_units())
     AllCUs.push_back(CU.get());
   if (AllCUs.empty())
     return {};
-  llvm::sort(AllCUs, [](const DWARFUnit *A, const DWARFUnit *B) {
-    return A->getOffset() < B->getOffset();
-  });
-  auto FindCuForOffset = [&](uint64_t Offset) -> DWARFUnit * {
-    auto *It =
-        llvm::upper_bound(AllCUs, Offset, [](uint64_t Off, DWARFUnit *U) {
-          return Off < U->getOffset();
-        });
-    if (It == AllCUs.begin())
-      return nullptr;
-    DWARFUnit *TargetCU = *--It;
-    // Ensure offset falls within TargetCU's range.
-    if (Offset >= TargetCU->getNextUnitOffset())
-      return nullptr;
-    return TargetCU;
-  };
 
   DenseSet<DWARFUnit *> CrossRefSet;
   EquivalenceClasses<DWARFUnit *> EC;
@@ -655,8 +639,8 @@ static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
             auto OptRef = Attr.Value.getAsDebugInfoReference();
             if (!OptRef)
               continue;
-            DWARFUnit *TargetCU = FindCuForOffset(*OptRef);
-            if (!TargetCU)
+            DWARFUnit *TargetCU = DwCtx.getUnitForOffset(*OptRef);
+            if (!TargetCU || TargetCU == CU)
               continue;
             if (CrossRefSet.insert(CU).second)
               EC.insert(CU);
@@ -702,9 +686,7 @@ static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx) {
 }
 
 static std::unordered_map<uint64_t, std::string>
-getDWONameMap(DWARFContext &DwCtx) {
-  const size_t Size =
-      std::distance(DwCtx.compile_units().begin(), DwCtx.compile_units().end());
+getDWONameMap(DWARFContext &DwCtx, const size_t Size) {
   std::unordered_map<uint64_t, std::string> DWOIDToNameMap(Size);
   std::unordered_map<std::string, uint32_t> NameToIndexMap(Size);
   for (const std::unique_ptr<DWARFUnit> &CU : DwCtx.compile_units()) {
@@ -1052,7 +1034,7 @@ void DWARFRewriter::updateDebugInfo() {
   if (!opts::DwarfOutputPath.empty() && !sys::fs::exists(opts::DwarfOutputPath))
     (void)sys::fs::create_directories(opts::DwarfOutputPath);
   std::unordered_map<uint64_t, std::string> DWOToNameMap =
-      getDWONameMap(*BC.DwCtx);
+      getDWONameMap(*BC.DwCtx, CUSize);
   DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
                                          *StrWriter);
   if (DebugNamesTable.isCreated())
@@ -1069,7 +1051,8 @@ void DWARFRewriter::updateDebugInfo() {
       *TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);
   CUOffsetMap OffsetMap =
       finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);
-  SmallVector<SmallVector<DWARFUnit *>> PartVec = partitionCUs(*BC.DwCtx);
+  SmallVector<SmallVector<DWARFUnit *>> PartVec =
+      partitionCUs(*BC.DwCtx, CUSize);
   const unsigned int ThreadCount =
       std::min(opts::DebugThreadCount, opts::ThreadCount);
   llvm::ThreadPoolInterface &ThreadPool =
@@ -1091,8 +1074,11 @@ void DWARFRewriter::updateDebugInfo() {
                                 DWOToNameMap, Accum);
     finalizeCompileUnits(*BucketDIEBlder, DIEBlder, *Streamer, OffsetMap,
                          BucketDIEBlder->getProcessedCUs(), *FinalAddrWriter);
+
+    // Release memory for this bucket.
     BucketDIEBlders[Idx].reset();
-    LocalWriters[Idx].clear();
+    LocalWriters[Idx].RngListsWriter.reset();
+    LocalWriters[Idx].LegacyRangesWriter.reset();
   };
 
   for (size_t I = 0; I < TotalTasks; ++I) {

>From c2cf9de9445f7a8b2cd94dabd90671098a7740f5 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Thu, 11 Jun 2026 16:55:35 +0800
Subject: [PATCH 10/10] refactor DWARFRewriter bucket merge flow

---
 bolt/include/bolt/Rewrite/DWARFRewriter.h |  20 ++-
 bolt/lib/Rewrite/DWARFRewriter.cpp        | 188 +++++++++++-----------
 2 files changed, 102 insertions(+), 106 deletions(-)

diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h
index f0f536fc0cbc0..3c6e90cf27e93 100644
--- a/bolt/include/bolt/Rewrite/DWARFRewriter.h
+++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h
@@ -143,16 +143,20 @@ class DWARFRewriter {
   CUOffsetMap finalizeTypeSections(DIEBuilder &DIEBlder, DIEStreamer &Streamer,
                                    GDBIndex &GDBIndexSection);
 
-  /// Finalize str section in the dwo
-  void finalizeSkeletonAndStrSection(
-      DIEBuilder &PartDIEBlder, DWARFUnit &CU,
+  /// Finalize str section in the dwo.
+  void finalizeMainCUStrOffsets(
+      DIEBuilder &PartDIEBlder, ArrayRef<DWARFUnit *> SortedCUs,
       const std::unordered_map<uint64_t, std::string> &DWOToNameMap);
 
-  /// Merge Bucket locs section and ranges section result
-  void mergePerBucketLocsAndRanges(
-      DIEBuilder &PartDIEBlder, BucketLocalWriter &LocalWriters,
-      const std::unordered_map<uint64_t, std::string> &DWOToNameMap,
-      BucketLocAccumOffset &Accum);
+  /// Merge Bucket locs section and ranges section result.
+  void mergePerBucketLocs(DIEBuilder &PartDIEBlder,
+                          ArrayRef<DWARFUnit *> SortedCUs,
+                          BucketLocAccumOffset &Accum);
+
+  /// Merge Bucket ranges section result.
+  void mergePerBucketRanges(DIEBuilder &PartDIEBlder,
+                            BucketLocalWriter &LocalWriters,
+                            ArrayRef<DWARFUnit *> SortedCUs);
 
   /// Process and write out CUs that are passed in.
   void finalizeCompileUnits(DIEBuilder &DIEBlder, DIEBuilder &MainDIEBlder,
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 254cb0ab20f9a..f352979b88c16 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -455,38 +455,6 @@ static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
   return false;
 }
 
-static void fixupDWARFRanges(DIEBuilder &DIEBlder, DWARFUnit &Unit,
-                             uint64_t Offset) {
-  if (Offset == 0)
-    return;
-
-  const uint16_t Version = Unit.getVersion();
-  if (Version >= 5) {
-    DIE *CUDie = DIEBlder.getUnitDIEbyUnit(Unit);
-    DIEValue RngBaseVal = CUDie->findAttribute(dwarf::DW_AT_rnglists_base);
-    if (RngBaseVal) {
-      const uint64_t OldVal = RngBaseVal.getDIEInteger().getValue();
-      DIEBlder.replaceValue(CUDie, dwarf::DW_AT_rnglists_base,
-                            RngBaseVal.getForm(), DIEInteger(OldVal + Offset));
-    }
-  }
-
-  const auto &DIs = DIEBlder.getDIEsByUnit(Unit);
-  for (const auto &DI : DIs) {
-    DIE *Die = DI->Die;
-    DIEValue RangesVal = Die->findAttribute(dwarf::DW_AT_ranges);
-    if (RangesVal &&
-        (Version < 5 || RangesVal.getForm() == dwarf::DW_FORM_sec_offset)) {
-      const uint64_t OldVal = RangesVal.getDIEInteger().getValue();
-      // Empty ranges are stored at offset 0 in both local and global
-      // buffers, so must not be shifted during merge.
-      if (OldVal != DebugRangesSectionWriter::getEmptyRangesOffset())
-        DIEBlder.replaceValue(Die, dwarf::DW_AT_ranges, RangesVal.getForm(),
-                              DIEInteger(OldVal + Offset));
-    }
-  }
-}
-
 static Expected<llvm::DWARFAddressRangesVector>
 getDIEAddressRanges(const DIE &Die, DWARFUnit &DU) {
   uint64_t LowPC, HighPC, Index;
@@ -719,55 +687,43 @@ getDWONameMap(DWARFContext &DwCtx, const size_t Size) {
   return DWOIDToNameMap;
 }
 
-void DWARFRewriter::finalizeSkeletonAndStrSection(
-    DIEBuilder &PartDIEBlder, DWARFUnit &CU,
+void DWARFRewriter::finalizeMainCUStrOffsets(
+    DIEBuilder &PartDIEBlder, ArrayRef<DWARFUnit *> SortedCUs,
     const std::unordered_map<uint64_t, std::string> &DWOToNameMap) {
-  const std::optional<uint64_t> DWOId = CU.getDWOId();
-  std::optional<DWARFUnit *> DWOCU = DWOId ? BC.getDWOCU(*DWOId) : std::nullopt;
-  const bool HasSplitCU = DWOCU && *DWOCU != nullptr;
-  const unsigned Version = CU.getVersion();
-
-  if (HasSplitCU) {
-    auto It = DWOToNameMap.find(*DWOId);
-    if (It != DWOToNameMap.end()) {
-      PartDIEBlder.updateDWONameCompDir(*StrOffstsWriter, *StrWriter, CU,
-                                        opts::DwarfOutputPath,
-                                        StringRef(It->second));
-      if (Version >= 5 && StrOffstsWriter->isStrOffsetsSectionModified())
-        StrOffstsWriter->finalizeSection(CU, PartDIEBlder);
+  for (DWARFUnit *CU : SortedCUs) {
+    const std::optional<uint64_t> DWOId = CU->getDWOId();
+    std::optional<DWARFUnit *> DWOCU =
+        DWOId ? BC.getDWOCU(*DWOId) : std::nullopt;
+    const bool HasSplitCU = DWOCU && *DWOCU != nullptr;
+    const unsigned Version = CU->getVersion();
+
+    if (HasSplitCU) {
+      auto It = DWOToNameMap.find(*DWOId);
+      if (It != DWOToNameMap.end()) {
+        PartDIEBlder.updateDWONameCompDir(*StrOffstsWriter, *StrWriter, *CU,
+                                          opts::DwarfOutputPath,
+                                          StringRef(It->second));
+        if (Version >= 5 && StrOffstsWriter->isStrOffsetsSectionModified())
+          StrOffstsWriter->finalizeSection(*CU, PartDIEBlder);
+      }
+      //  split CU were just finalized above when the
+      // .debug_str_offsets section was modified;
+      return;
     }
-    //  split CU were just finalized above when the
-    // .debug_str_offsets section was modified;
-    return;
-  }
-
-  if (Version >= 5)
-    StrOffstsWriter->finalizeSection(CU, PartDIEBlder);
-}
 
-/// Merge: compute per-CU loclist/legacy-loc bases and in the
-/// same loop apply DWARF5 rnglists/loclists base fixups and legacy
-/// .debug_loc base fixups.
-void DWARFRewriter::mergePerBucketLocsAndRanges(
-    DIEBuilder &PartDIEBlder, BucketLocalWriter &LocalWriters,
-    const std::unordered_map<uint64_t, std::string> &DWOToNameMap,
-    BucketLocAccumOffset &Accum) {
-  const auto &Processed = PartDIEBlder.getProcessedCUs();
-  std::vector<DWARFUnit *> SortedCUs;
-  SortedCUs.assign(Processed.begin(), Processed.end());
-  // Ensure deterministic output by sorting CUs in section offset order.
-  llvm::sort(SortedCUs, [](const DWARFUnit *A, const DWARFUnit *B) {
-    return A->getOffset() < B->getOffset();
-  });
-
-  for (DWARFUnit *CU : SortedCUs) {
-    // record CUs order to make loc/loclist order correct
-    Accum.LocListCUOrder.push_back(CU->getOffset());
-    finalizeSkeletonAndStrSection(PartDIEBlder, *CU, DWOToNameMap);
+    if (Version >= 5)
+      StrOffstsWriter->finalizeSection(*CU, PartDIEBlder);
   }
+}
+void DWARFRewriter::mergePerBucketLocs(DIEBuilder &PartDIEBlder,
+                                       ArrayRef<DWARFUnit *> SortedCUs,
+                                       BucketLocAccumOffset &Accum) {
+  // Location lists are emitted later in Accum.LocListCUOrder order. Compute the
+  // global base each CU will have in the final section and update DIEs now.
   for (DWARFUnit *CU : SortedCUs) {
     const uint64_t CUOffset = CU->getOffset();
-    // Compute this CU's base and adjust loclists / legacy loc fixups inline.
+    // Record CUs order to make loc/loclist order correct.
+    Accum.LocListCUOrder.push_back(CUOffset);
     uint64_t LoclistsBase = 0;
     uint64_t LegacyLocBase = 0;
 
@@ -794,31 +750,44 @@ void DWARFRewriter::mergePerBucketLocsAndRanges(
       }
     }
 
-    if (CU->getVersion() >= 5) {
-      const uint64_t RangsOffset = RangeListsSectionWriter->getSectionOffset();
-      fixupDWARFRanges(PartDIEBlder, *CU, RangsOffset);
-
-      if (LoclistsBase) {
-        DIE *UnitDIE = PartDIEBlder.getUnitDIEbyUnit(*CU);
-        if (UnitDIE) {
-          DIEValue LocBase = UnitDIE->findAttribute(dwarf::DW_AT_loclists_base);
-          if (LocBase.getType()) {
-            const uint64_t OldVal = LocBase.getDIEInteger().getValue();
-            PartDIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_loclists_base,
-                                      LocBase.getForm(),
-                                      DIEInteger(OldVal + LoclistsBase));
-          }
+    if (LoclistsBase) {
+      DIE *UnitDIE = PartDIEBlder.getUnitDIEbyUnit(*CU);
+      if (UnitDIE) {
+        DIEValue LocBase = UnitDIE->findAttribute(dwarf::DW_AT_loclists_base);
+        if (LocBase.getType()) {
+          const uint64_t OldVal = LocBase.getDIEInteger().getValue();
+          PartDIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_loclists_base,
+                                    LocBase.getForm(),
+                                    DIEInteger(OldVal + LoclistsBase));
         }
       }
-      continue;
     }
-
-    if (LegacyLocBase && LocIt != LocListWritersByCU.end())
+    if (LegacyLocBase)
       LocIt->second->applyBase(PartDIEBlder, LegacyLocBase);
   }
+}
+
+void DWARFRewriter::mergePerBucketRanges(DIEBuilder &PartDIEBlder,
+                                         BucketLocalWriter &LocalWriters,
+                                         ArrayRef<DWARFUnit *> SortedCUs) {
+  // Range lists are stored in bucket-local buffers during parallel processing.
+  // Fix DIE offsets from bucket-local coordinates into the current global
+  // range section coordinates, then append the local buffers.
+  if (RangeListsSectionWriter) {
+    const uint64_t RangesOffset = RangeListsSectionWriter->getSectionOffset();
+    for (DWARFUnit *CU : SortedCUs)
+      if (CU->getVersion() >= 5) {
+        DIE *CUDie = PartDIEBlder.getUnitDIEbyUnit(*CU);
+        DIEValue RngBaseVal = CUDie->findAttribute(dwarf::DW_AT_rnglists_base);
+        if (RngBaseVal) {
+          const uint64_t OldVal = RngBaseVal.getDIEInteger().getValue();
+          PartDIEBlder.replaceValue(CUDie, dwarf::DW_AT_rnglists_base,
+                                    RngBaseVal.getForm(),
+                                    DIEInteger(OldVal + RangesOffset));
+        }
+      }
+  }
 
-  /// Append this bucket's .debug_rnglists and .debug_ranges local buffers to
-  /// the global writers.
   if (LocalWriters.RngListsWriter && RangeListsSectionWriter) {
     std::unique_ptr<DebugBufferVector> LocalBuf =
         LocalWriters.RngListsWriter->releaseBuffer();
@@ -838,8 +807,23 @@ void DWARFRewriter::mergePerBucketLocsAndRanges(
   const uint64_t GlobalOffset =
       LegacyRangesSectionWriter->getSectionOffset() - LocalTerminatorSize;
   for (DWARFUnit *CU : SortedCUs)
-    if (CU->getVersion() <= 4)
-      fixupDWARFRanges(PartDIEBlder, *CU, GlobalOffset);
+    if (CU->getVersion() <= 4) {
+      // Fix DWARF v4 DW_AT_ranges attribute.
+      const auto &DIs = PartDIEBlder.getDIEsByUnit(*CU);
+      for (const auto &DI : DIs) {
+        DIE *Die = DI->Die;
+        DIEValue RangesVal = Die->findAttribute(dwarf::DW_AT_ranges);
+        if (RangesVal && RangesVal.getForm() == dwarf::DW_FORM_sec_offset) {
+          const uint64_t OldVal = RangesVal.getDIEInteger().getValue();
+          // Empty ranges are stored at offset 0 in both local and global
+          // buffers, so must not be shifted during merge.
+          if (OldVal != DebugRangesSectionWriter::getEmptyRangesOffset())
+            PartDIEBlder.replaceValue(Die, dwarf::DW_AT_ranges,
+                                      RangesVal.getForm(),
+                                      DIEInteger(OldVal + GlobalOffset));
+        }
+      }
+    }
   DebugBufferVector TrimmedBuf(LegacyBuf->begin() + LocalTerminatorSize,
                                LegacyBuf->end());
   LegacyRangesSectionWriter->appendToRangeBuffer(TrimmedBuf);
@@ -1069,9 +1053,17 @@ void DWARFRewriter::updateDebugInfo() {
   auto MergeBucket = [&](size_t Idx) -> void {
     std::unique_ptr<DIEBuilder> &BucketDIEBlder = BucketDIEBlders[Idx];
     assert(BucketDIEBlder && "Bucket DIEBuilder is null.");
+    const auto &Processed = BucketDIEBlder->getProcessedCUs();
+    std::vector<DWARFUnit *> SortedCUs;
+    SortedCUs.assign(Processed.begin(), Processed.end());
+    // Ensure deterministic output by sorting CUs in section offset order.
+    llvm::sort(SortedCUs, [](const DWARFUnit *A, const DWARFUnit *B) {
+      return A->getOffset() < B->getOffset();
+    });
 
-    mergePerBucketLocsAndRanges(*BucketDIEBlder, LocalWriters[Idx],
-                                DWOToNameMap, Accum);
+    finalizeMainCUStrOffsets(*BucketDIEBlder, SortedCUs, DWOToNameMap);
+    mergePerBucketLocs(*BucketDIEBlder, SortedCUs, Accum);
+    mergePerBucketRanges(*BucketDIEBlder, LocalWriters[Idx], SortedCUs);
     finalizeCompileUnits(*BucketDIEBlder, DIEBlder, *Streamer, OffsetMap,
                          BucketDIEBlder->getProcessedCUs(), *FinalAddrWriter);
 



More information about the llvm-commits mailing list