diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp index b0ff89da97123..d3a888507031b 100644 --- a/llvm/tools/llvm-objdump/SourcePrinter.cpp +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -50,11 +50,6 @@ void InlinedFunction::dump(raw_ostream &OS) const { void InlinedFunction::printElementLine(raw_ostream &OS, object::SectionedAddress Addr, bool IsEnd) const { - bool LiveIn = !IsEnd && Range.LowPC == Addr.Address; - bool LiveOut = IsEnd && Range.HighPC == Addr.Address; - if (!(LiveIn || LiveOut)) - return; - uint32_t CallFile, CallLine, CallColumn, CallDiscriminator; InlinedFuncDie.getCallerFrame(CallFile, CallLine, CallColumn, CallDiscriminator); @@ -126,8 +121,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( InlinedFuncName, U, FuncDie, InlinedFuncDie, Range)); + // Map the element's low address (LowPC) to its pointer for fast range start + // lookup. + LiveElementsByAddress[FuncLowPC].push_back(LiveElements.back().get()); + // Map the element's high address (HighPC) to its pointer for fast range end + // lookup. + LiveElementsByEndAddress[FuncHighPC].push_back(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 +152,9 @@ void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { } for (const DWARFLocationExpression &LocExpr : *Locs) { + std::unique_ptr NewVar; if (LocExpr.Range) { - LiveElements.emplace_back( - std::make_unique(LocExpr, VarName, U, FuncDie)); + NewVar = std::make_unique(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 +162,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(WholeFuncExpr, VarName, U, FuncDie)); + NewVar = + std::make_unique(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(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[CurrentVar->getLocExpr().Range->LowPC].push_back( + CurrentVar); + LiveElementsByEndAddress[CurrentVar->getLocExpr().Range->HighPC] + .push_back(CurrentVar); + } } } } @@ -205,14 +226,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; + } - size_t OldSize = ActiveCols.size(); - ActiveCols.grow(std::max(OldSize * 2, 1)); - return OldSize; + 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; + + // Remove the element's entry from the map and add the column to the free + // list. + ElementToColumn.erase(ElementIdx); + FreeCols.insert(ColIdx); +} + +std::vector +LiveElementPrinter::getSortedActiveElementIndices() const { + // Get all ElementIdx values that currently have an assigned column. + std::vector 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 +302,116 @@ 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 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 &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> NewLiveElements; + auto CollectNewElements = [&](const auto &It) { + if (It == LiveElementsByAddress.end()) + return; + + const std::vector &ElementList = It->second; + // Get the ElementIdx for sorting and column management. + for (LiveElement *LE : ElementList) { + 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.find(ThisAddr.Address)); + // Collect elements starting at NextAddr (the address immediately + // following the instruction). + CollectNewElements(LiveElementsByAddress.find(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 &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 +482,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 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 +504,7 @@ void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS, OS << " "; } - const std::unique_ptr &LE = - LiveElements[ActiveCols[ColIdx].ElementIdx]; + const std::unique_ptr &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 +568,60 @@ 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 &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. + std::vector ElementIndices; + auto It = LiveElementsByAddress.find(Addr.Address); + if (It != LiveElementsByAddress.end()) { + for (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 &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. + std::vector ElementIndices; + auto It = LiveElementsByEndAddress.find(Addr.Address); + if (It != LiveElementsByEndAddress.end()) { + for (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..24be2ff92ec5a 100644 --- a/llvm/tools/llvm-objdump/SourcePrinter.h +++ b/llvm/tools/llvm-objdump/SourcePrinter.h @@ -10,6 +10,7 @@ #define LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H #include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" @@ -78,6 +79,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 +99,23 @@ class LiveElementPrinter { std::numeric_limits::max(); }; - // All live elements we know about in the object/image file. + // Vector that owns all LiveElement objects for memory management. std::vector> LiveElements; - - // The columns we are currently drawing. - IndexedMap ActiveCols; + // Map for fast lookup of live elements by their starting address (LowPC). + llvm::MapVector> LiveElementsByAddress; + // Map for fast lookup of live elements by their ending address (HighPC). + llvm::MapVector> + LiveElementsByEndAddress; + // Map from a LiveElement pointer to its index in the LiveElements vector. + std::unordered_map ElementPtrToIndex; + // Map from a live element index to column index for efficient lookup. + std::unordered_map ElementToColumn; + // Vector of columns currently used for printing live ranges. + std::vector ActiveCols; + // Set of available column indices kept sorted for efficient reuse. + std::set FreeCols; + // Vector of available column indices that can be reused. + std::vector ColumnsToFreeNextCycle; const MCRegisterInfo &MRI; const MCSubtargetInfo &STI; @@ -122,11 +136,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 getSortedActiveElementIndices() const; public: LiveElementPrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) - : ActiveCols(Column()), MRI(MRI), STI(STI) {} + : MRI(MRI), STI(STI) {} void dump() const;