[llvm] [llvm-objdump] Optimize live element tracking (PR #158763)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 16 10:11:03 PDT 2025


https://github.com/gulfemsavrun updated https://github.com/llvm/llvm-project/pull/158763

>From 52720f9e894b3e16e07fb8cd609fadb9ec1b95e9 Mon Sep 17 00:00:00 2001
From: Gulfem Savrun Yeniceri <gulfem at google.com>
Date: Mon, 15 Sep 2025 18:18:02 -0700
Subject: [PATCH] [llvm-objdump] Optimize live element tracking

This patch significantly optimizes the LiveElementPrinter
by replacing a slow linear search with efficient hash map
lookups. It refactors the code to use a map-based system
for tracking live element addresses and managing column
assignments, leading to a major performance improvement
for large binaries.
---
 llvm/tools/llvm-objdump/SourcePrinter.cpp | 272 ++++++++++++++++++----
 llvm/tools/llvm-objdump/SourcePrinter.h   |  32 ++-
 2 files changed, 248 insertions(+), 56 deletions(-)

diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
index b0ff89da97123..fdc9357cc8fc2 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.cpp
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -52,6 +52,8 @@ void InlinedFunction::printElementLine(raw_ostream &OS,
                                        bool IsEnd) const {
   bool LiveIn = !IsEnd && Range.LowPC == Addr.Address;
   bool LiveOut = IsEnd && Range.HighPC == Addr.Address;
+  // This check is technically redundant as the function is only called when
+  // either a start or end address matches, but it serves as a small safeguard.
   if (!(LiveIn || LiveOut))
     return;
 
@@ -126,8 +128,18 @@ void LiveElementPrinter::addInlinedFunction(DWARFDie FuncDie,
   DWARFUnit *U = InlinedFuncDie.getDwarfUnit();
   const char *InlinedFuncName = InlinedFuncDie.getName(DINameKind::LinkageName);
   DWARFAddressRange Range{FuncLowPC, FuncHighPC, SectionIndex};
+  // Add the new element to the main vector.
   LiveElements.emplace_back(std::make_unique<InlinedFunction>(
       InlinedFuncName, U, FuncDie, InlinedFuncDie, Range));
+  // Map the element's low address (LowPC) to its pointer for fast range start
+  // lookup.
+  LiveElementsByAddress.emplace(FuncLowPC, LiveElements.back().get());
+  // Map the element's high address (HighPC) to its pointer for fast range end
+  // lookup.
+  LiveElementsByEndAddress.emplace(FuncHighPC, LiveElements.back().get());
+  // Map the pointer to its DWARF discovery index (ElementIdx) for deterministic
+  // ordering.
+  ElementPtrToIndex[LiveElements.back().get()] = LiveElements.size() - 1;
 }
 
 void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
@@ -147,9 +159,9 @@ void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
   }
 
   for (const DWARFLocationExpression &LocExpr : *Locs) {
+    std::unique_ptr<LiveVariable> NewVar;
     if (LocExpr.Range) {
-      LiveElements.emplace_back(
-          std::make_unique<LiveVariable>(LocExpr, VarName, U, FuncDie));
+      NewVar = std::make_unique<LiveVariable>(LocExpr, VarName, U, FuncDie);
     } else {
       // If the LocExpr does not have an associated range, it is valid for
       // the whole of the function.
@@ -157,8 +169,24 @@ void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
       // LocExpr, does that happen in reality?
       DWARFLocationExpression WholeFuncExpr{
           DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr};
-      LiveElements.emplace_back(
-          std::make_unique<LiveVariable>(WholeFuncExpr, VarName, U, FuncDie));
+      NewVar =
+          std::make_unique<LiveVariable>(WholeFuncExpr, VarName, U, FuncDie);
+    }
+
+    // Add the new variable to all the data structures.
+    if (NewVar) {
+      LiveElements.emplace_back(std::move(NewVar));
+      LiveVariable *CurrentVar =
+          static_cast<LiveVariable *>(LiveElements.back().get());
+      // Map from a LiveElement pointer to its index in the LiveElements vector.
+      ElementPtrToIndex.emplace(CurrentVar, LiveElements.size() - 1);
+      if (CurrentVar->getLocExpr().Range) {
+        // Add the variable to address-based maps.
+        LiveElementsByAddress.emplace(CurrentVar->getLocExpr().Range->LowPC,
+                                      CurrentVar);
+        LiveElementsByEndAddress.emplace(CurrentVar->getLocExpr().Range->HighPC,
+                                         CurrentVar);
+      }
     }
   }
 }
@@ -205,14 +233,56 @@ unsigned LiveElementPrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) {
   return FirstUnprintedLogicalColumn;
 }
 
-unsigned LiveElementPrinter::findFreeColumn() {
-  for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
-    if (!ActiveCols[ColIdx].isActive())
-      return ColIdx;
+unsigned LiveElementPrinter::getOrCreateColumn(unsigned ElementIdx) {
+  // Check if the element already has an assigned column.
+  auto it = ElementToColumn.find(ElementIdx);
+  if (it != ElementToColumn.end()) {
+    return it->second;
+  }
+
+  unsigned ColIdx;
+  if (!FreeCols.empty()) {
+    // Get the smallest available index from the set.
+    ColIdx = *FreeCols.begin();
+    // Remove the index from the set.
+    FreeCols.erase(FreeCols.begin());
+  } else {
+    // No free columns, so create a new one.
+    ColIdx = ActiveCols.size();
+    ActiveCols.emplace_back();
+  }
+
+  // Assign the element to the column and update the map.
+  ElementToColumn[ElementIdx] = ColIdx;
+  ActiveCols[ColIdx].ElementIdx = ElementIdx;
+  return ColIdx;
+}
+
+void LiveElementPrinter::freeColumn(unsigned ColIdx) {
+  unsigned ElementIdx = ActiveCols[ColIdx].ElementIdx;
+
+  // Clear the column's data and add it to the free list.
+  ActiveCols[ColIdx].ElementIdx = Column::NullElementIdx;
+  ActiveCols[ColIdx].LiveIn = false;
+  ActiveCols[ColIdx].LiveOut = false;
+  ActiveCols[ColIdx].MustDrawLabel = false;
 
-  size_t OldSize = ActiveCols.size();
-  ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
-  return OldSize;
+  // Remove the element's entry from the map and add the column to the free
+  // list.
+  ElementToColumn.erase(ElementIdx);
+  FreeCols.insert(ColIdx);
+}
+
+std::vector<unsigned>
+LiveElementPrinter::getSortedActiveElementIndices() const {
+  // Get all ElementIdx values that currently have an assigned column.
+  std::vector<unsigned> Indices;
+  for (const auto &Pair : ElementToColumn)
+    Indices.push_back(Pair.first);
+
+  // Sort by ElementIdx, which is the DWARF discovery order.
+  llvm::stable_sort(Indices);
+  return Indices;
 }
 
 void LiveElementPrinter::dump() const {
@@ -239,57 +309,115 @@ void LiveElementPrinter::addCompileUnit(DWARFDie D) {
 void LiveElementPrinter::update(object::SectionedAddress ThisAddr,
                                 object::SectionedAddress NextAddr,
                                 bool IncludeDefinedVars) {
-  // Do not create live ranges when debug-inlined-funcs option is provided with
-  // line format option.
+  // Exit early if only printing function limits.
   if (DbgInlinedFunctions == DFLimitsOnly)
     return;
 
-  // First, check variables which have already been assigned a column, so
-  // that we don't change their order.
-  SmallSet<unsigned, 8> CheckedElementIdxs;
+  // Free columns identified in the previous cycle.
+  for (unsigned ColIdx : ColumnsToFreeNextCycle)
+    freeColumn(ColIdx);
+  ColumnsToFreeNextCycle.clear();
+
+  // Update status of active columns and collect those to free next cycle.
   for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
     if (!ActiveCols[ColIdx].isActive())
       continue;
 
-    CheckedElementIdxs.insert(ActiveCols[ColIdx].ElementIdx);
     const std::unique_ptr<LiveElement> &LE =
         LiveElements[ActiveCols[ColIdx].ElementIdx];
     ActiveCols[ColIdx].LiveIn = LE->liveAtAddress(ThisAddr);
     ActiveCols[ColIdx].LiveOut = LE->liveAtAddress(NextAddr);
-    std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
-    LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
-                      << NextAddr.Address << ", " << Name << ", Col " << ColIdx
-                      << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
-                      << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
 
-    if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
+    LLVM_DEBUG({
+      std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
+      dbgs() << "pass 1, " << ThisAddr.Address << "-" << NextAddr.Address
+             << ", " << Name << ", Col " << ColIdx
+             << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
+             << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n";
+    });
+
+    // If element is fully dead, deactivate column immediately.
+    if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) {
       ActiveCols[ColIdx].ElementIdx = Column::NullElementIdx;
+      continue;
+    }
+
+    // Mark for cleanup in the next cycle if range ends here.
+    if (ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
+      ColumnsToFreeNextCycle.push_back(ColIdx);
   }
 
   // Next, look for variables which don't already have a column, but which
-  // are now live.
+  // are now live (those starting at ThisAddr or NextAddr).
   if (IncludeDefinedVars) {
-    for (unsigned ElementIdx = 0, End = LiveElements.size(); ElementIdx < End;
-         ++ElementIdx) {
-      if (CheckedElementIdxs.count(ElementIdx))
+    // Collect all elements starting at ThisAddr and NextAddr.
+    std::vector<std::pair<unsigned, LiveElement *>> NewLiveElements;
+    // Process elements from a map range and add them to NewLiveElements.
+    auto CollectNewElements = [&](const auto &Range) {
+      for (auto it = Range.first; it != Range.second; ++it) {
+        LiveElement *LE = it->second;
+
+        // Get the ElementIdx for sorting and column management.
+        auto IndexIt = ElementPtrToIndex.find(LE);
+        if (IndexIt == ElementPtrToIndex.end()) {
+          LLVM_DEBUG(
+              dbgs()
+              << "Error: LiveElement in map but not in ElementPtrToIndex!\n");
+          continue;
+        }
+
+        unsigned ElementIdx = IndexIt->second;
+        // Skip elements that already have a column.
+        if (ElementToColumn.count(ElementIdx))
+          continue;
+
+        bool LiveIn = LE->liveAtAddress(ThisAddr);
+        bool LiveOut = LE->liveAtAddress(NextAddr);
+        if (!LiveIn && !LiveOut)
+          continue;
+
+        NewLiveElements.emplace_back(ElementIdx, LE);
+      }
+    };
+
+    // Collect elements starting at ThisAddr.
+    CollectNewElements(LiveElementsByAddress.equal_range(ThisAddr.Address));
+    // Collect elements starting at NextAddr (the address immediately following
+    // the instruction).
+    CollectNewElements(LiveElementsByAddress.equal_range(NextAddr.Address));
+    // Sort elements by DWARF discovery order (ElementIdx) for deterministic
+    // column assignment.
+    llvm::stable_sort(NewLiveElements, [](const auto &A, const auto &B) {
+      return A.first < B.first;
+    });
+
+    // Assign columns in deterministic order.
+    for (const auto &ElementPair : NewLiveElements) {
+      unsigned ElementIdx = ElementPair.first;
+      // Skip if element was already added from the first range.
+      if (ElementToColumn.count(ElementIdx))
         continue;
 
-      const std::unique_ptr<LiveElement> &LE = LiveElements[ElementIdx];
+      LiveElement *LE = ElementPair.second;
       bool LiveIn = LE->liveAtAddress(ThisAddr);
       bool LiveOut = LE->liveAtAddress(NextAddr);
-      if (!LiveIn && !LiveOut)
-        continue;
 
-      unsigned ColIdx = findFreeColumn();
-      std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
-      LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
-                        << NextAddr.Address << ", " << Name << ", Col "
-                        << ColIdx << ": LiveIn=" << LiveIn
-                        << ", LiveOut=" << LiveOut << "\n");
-      ActiveCols[ColIdx].ElementIdx = ElementIdx;
+      // Assign or create a column.
+      unsigned ColIdx = getOrCreateColumn(ElementIdx);
+      LLVM_DEBUG({
+        std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
+        dbgs() << "pass 2, " << ThisAddr.Address << "-" << NextAddr.Address
+               << ", " << Name << ", Col " << ColIdx << ": LiveIn=" << LiveIn
+               << ", LiveOut=" << LiveOut << "\n";
+      });
+
       ActiveCols[ColIdx].LiveIn = LiveIn;
       ActiveCols[ColIdx].LiveOut = LiveOut;
       ActiveCols[ColIdx].MustDrawLabel = true;
+
+      // Mark for cleanup next cycle if range ends here.
+      if (ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
+        ColumnsToFreeNextCycle.push_back(ColIdx);
     }
   }
 }
@@ -360,7 +488,14 @@ void LiveElementPrinter::printAfterOtherLine(formatted_raw_ostream &OS,
 void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS,
                                            bool MustPrint) {
   bool PrintedSomething = false;
-  for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+  // Get all active elements, sorted by discovery order (ElementIdx).
+  std::vector<unsigned> SortedElementIndices = getSortedActiveElementIndices();
+  // The outer loop iterates over the deterministic DWARF discovery order
+  // (ElementIdx).
+  for (unsigned ElementIdx : SortedElementIndices) {
+    // Look up the physical column index (ColIdx) assigned to this
+    // element. We use .at() because we are certain the element is active.
+    unsigned ColIdx = ElementToColumn.at(ElementIdx);
     if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
       // First we need to print the live range markers for any active
       // columns to the left of this one.
@@ -375,8 +510,7 @@ void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS,
           OS << "  ";
       }
 
-      const std::unique_ptr<LiveElement> &LE =
-          LiveElements[ActiveCols[ColIdx].ElementIdx];
+      const std::unique_ptr<LiveElement> &LE = LiveElements[ElementIdx];
       // Then print the variable name and location of the new live range,
       // with box drawing characters joining it to the live range line.
       OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive
@@ -440,20 +574,58 @@ void LiveElementPrinter::printAfterInst(formatted_raw_ostream &OS) {
 
 void LiveElementPrinter::printStartLine(formatted_raw_ostream &OS,
                                         object::SectionedAddress Addr) {
-  // Print a line to idenfity the start of an inlined function if line format
-  // is specified.
-  if (DbgInlinedFunctions == DFLimitsOnly)
-    for (const std::unique_ptr<LiveElement> &LE : LiveElements)
-      LE->printElementLine(OS, Addr, false);
+  // Only print the start line for inlined functions if DFLimitsOnly is
+  // enabled.
+  if (DbgInlinedFunctions != DFLimitsOnly)
+    return;
+
+  // Use the map to find all elements that start at the given address.
+  auto Range = LiveElementsByAddress.equal_range(Addr.Address);
+  std::vector<unsigned> ElementIndices;
+  for (auto it = Range.first; it != Range.second; ++it) {
+    LiveElement *LE = it->second;
+    // Look up the ElementIdx from the pointer.
+    auto IndexIt = ElementPtrToIndex.find(LE);
+    if (IndexIt != ElementPtrToIndex.end())
+      ElementIndices.push_back(IndexIt->second);
+  }
+
+  // Sort the indices to ensure deterministic output order (by DWARF discovery
+  // order).
+  llvm::stable_sort(ElementIndices);
+
+  for (unsigned ElementIdx : ElementIndices) {
+    LiveElement *LE = LiveElements[ElementIdx].get();
+    LE->printElementLine(OS, Addr, false);
+  }
 }
 
 void LiveElementPrinter::printEndLine(formatted_raw_ostream &OS,
                                       object::SectionedAddress Addr) {
-  // Print a line to idenfity the end of an inlined function if line format is
-  // specified.
-  if (DbgInlinedFunctions == DFLimitsOnly)
-    for (const std::unique_ptr<LiveElement> &LE : LiveElements)
-      LE->printElementLine(OS, Addr, true);
+  // Only print the end line for inlined functions if DFLimitsOnly is
+  // enabled.
+  if (DbgInlinedFunctions != DFLimitsOnly)
+    return;
+
+  // Use the map to find elements that end at the given address.
+  auto Range = LiveElementsByEndAddress.equal_range(Addr.Address);
+  std::vector<unsigned> ElementIndices;
+  for (auto it = Range.first; it != Range.second; ++it) {
+    LiveElement *LE = it->second;
+    // Look up the ElementIdx from the pointer.
+    auto IndexIt = ElementPtrToIndex.find(LE);
+    if (IndexIt != ElementPtrToIndex.end())
+      ElementIndices.push_back(IndexIt->second);
+  }
+
+  // Sort the indices to ensure deterministic output order (by DWARF discovery
+  // order).
+  llvm::stable_sort(ElementIndices);
+
+  for (unsigned ElementIdx : ElementIndices) {
+    LiveElement *LE = LiveElements[ElementIdx].get();
+    LE->printElementLine(OS, Addr, true);
+  }
 }
 
 bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h
index 5c131a0eb1fd7..d70d9ea29d7b7 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.h
+++ b/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -78,6 +78,7 @@ class LiveVariable : public LiveElement {
   bool liveAtAddress(object::SectionedAddress Addr) const override;
   void print(raw_ostream &OS, const MCRegisterInfo &MRI) const override;
   void dump(raw_ostream &OS) const override;
+  const DWARFLocationExpression &getLocExpr() const { return LocExpr; }
 };
 
 /// Helper class for printing source locations for variables and inlined
@@ -97,11 +98,22 @@ class LiveElementPrinter {
         std::numeric_limits<unsigned>::max();
   };
 
-  // All live elements we know about in the object/image file.
+  // Vector that owns all LiveElement objects for memory management.
   std::vector<std::unique_ptr<LiveElement>> LiveElements;
-
-  // The columns we are currently drawing.
-  IndexedMap<Column> ActiveCols;
+  // Map for fast lookup of live elements by their starting address (LowPC).
+  std::unordered_multimap<uint64_t, LiveElement *> LiveElementsByAddress;
+  // Map for fast lookup of live elements by their ending address (HighPC).
+  std::unordered_multimap<uint64_t, LiveElement *> LiveElementsByEndAddress;
+  // Map from a LiveElement pointer to its index in the LiveElements vector.
+  std::unordered_map<LiveElement *, unsigned> ElementPtrToIndex;
+  // Map from a live element index to column index for efficient lookup.
+  std::unordered_map<unsigned, unsigned> ElementToColumn;
+  // Vector of columns currently used for printing live ranges.
+  std::vector<Column> ActiveCols;
+  // Set of available column indices kept sorted for efficient reuse.
+  std::set<unsigned> FreeCols;
+  // Vector of available column indices that can be reused.
+  std::vector<unsigned> ColumnsToFreeNextCycle;
 
   const MCRegisterInfo &MRI;
   const MCSubtargetInfo &STI;
@@ -122,11 +134,19 @@ class LiveElementPrinter {
   // put live element lines. Pick a less overloaded word.
   unsigned moveToFirstVarColumn(formatted_raw_ostream &OS);
 
-  unsigned findFreeColumn();
+  // Get an existing column for a live element, or find a free one.
+  unsigned getOrCreateColumn(unsigned ElementIdx);
+
+  // Free a column when its element is no longer live.
+  void freeColumn(unsigned ColIdx);
+
+  // Returns the indices of all currently active elements, sorted by their DWARF
+  // discovery order (ElementIdx).
+  std::vector<unsigned> getSortedActiveElementIndices() const;
 
 public:
   LiveElementPrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI)
-      : ActiveCols(Column()), MRI(MRI), STI(STI) {}
+      : MRI(MRI), STI(STI) {}
 
   void dump() const;
 



More information about the llvm-commits mailing list