Skip to content

Commit 4383874

Browse files
gulfemsavrungithub-actions[bot]
authored andcommitted
Automerge: [llvm-objdump] Optimize live element tracking (#158763)
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.
2 parents cb2de7e + 49516ba commit 4383874

File tree

3 files changed

+251
-71
lines changed

3 files changed

+251
-71
lines changed

llvm/tools/llvm-objdump/SourcePrinter.cpp

Lines changed: 203 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,6 @@ void InlinedFunction::dump(raw_ostream &OS) const {
5050
void InlinedFunction::printElementLine(raw_ostream &OS,
5151
object::SectionedAddress Addr,
5252
bool IsEnd) const {
53-
bool LiveIn = !IsEnd && Range.LowPC == Addr.Address;
54-
bool LiveOut = IsEnd && Range.HighPC == Addr.Address;
55-
if (!(LiveIn || LiveOut))
56-
return;
57-
5853
uint32_t CallFile, CallLine, CallColumn, CallDiscriminator;
5954
InlinedFuncDie.getCallerFrame(CallFile, CallLine, CallColumn,
6055
CallDiscriminator);
@@ -126,8 +121,41 @@ void LiveElementPrinter::addInlinedFunction(DWARFDie FuncDie,
126121
DWARFUnit *U = InlinedFuncDie.getDwarfUnit();
127122
const char *InlinedFuncName = InlinedFuncDie.getName(DINameKind::LinkageName);
128123
DWARFAddressRange Range{FuncLowPC, FuncHighPC, SectionIndex};
124+
// Add the new element to the main vector.
129125
LiveElements.emplace_back(std::make_unique<InlinedFunction>(
130126
InlinedFuncName, U, FuncDie, InlinedFuncDie, Range));
127+
128+
LiveElement *LE = LiveElements.back().get();
129+
// Map the element's low address (LowPC) to its pointer for fast range start
130+
// lookup.
131+
LiveElementsByAddress[FuncLowPC].push_back(LE);
132+
// Map the element's high address (HighPC) to its pointer for fast range end
133+
// lookup.
134+
LiveElementsByEndAddress[FuncHighPC].push_back(LE);
135+
// Map the pointer to its DWARF discovery index for deterministic
136+
// ordering.
137+
ElementPtrToIndex[LE] = LiveElements.size() - 1;
138+
}
139+
140+
/// Registers the most recently added LiveVariable into all data structures.
141+
void LiveElementPrinter::registerNewVariable() {
142+
assert(
143+
!LiveElements.empty() &&
144+
"registerNewVariable called before element was added to LiveElements.");
145+
LiveVariable *CurrentVar =
146+
static_cast<LiveVariable *>(LiveElements.back().get());
147+
assert(ElementPtrToIndex.count(CurrentVar) == 0 &&
148+
"Element already registered!");
149+
150+
// Map from a LiveElement pointer to its index in the LiveElements.
151+
ElementPtrToIndex[CurrentVar] = LiveElements.size() - 1;
152+
153+
if (const std::optional<DWARFAddressRange> &Range =
154+
CurrentVar->getLocExpr().Range) {
155+
// Add the variable to address-based maps.
156+
LiveElementsByAddress[Range->LowPC].push_back(CurrentVar);
157+
LiveElementsByEndAddress[Range->HighPC].push_back(CurrentVar);
158+
}
131159
}
132160

133161
void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
@@ -160,6 +188,9 @@ void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
160188
LiveElements.emplace_back(
161189
std::make_unique<LiveVariable>(WholeFuncExpr, VarName, U, FuncDie));
162190
}
191+
192+
// Register the new variable with all data structures.
193+
registerNewVariable();
163194
}
164195
}
165196

@@ -205,14 +236,52 @@ unsigned LiveElementPrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) {
205236
return FirstUnprintedLogicalColumn;
206237
}
207238

208-
unsigned LiveElementPrinter::findFreeColumn() {
209-
for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
210-
if (!ActiveCols[ColIdx].isActive())
211-
return ColIdx;
239+
unsigned LiveElementPrinter::getOrCreateColumn(unsigned ElementIdx) {
240+
// Check if the element already has an assigned column.
241+
auto it = ElementToColumn.find(ElementIdx);
242+
if (it != ElementToColumn.end())
243+
return it->second;
244+
245+
unsigned ColIdx;
246+
if (!FreeCols.empty()) {
247+
// Get the smallest available index from the set.
248+
ColIdx = *FreeCols.begin();
249+
// Remove the index from the set.
250+
FreeCols.erase(FreeCols.begin());
251+
} else {
252+
// No free columns, so create a new one.
253+
ColIdx = ActiveCols.size();
254+
ActiveCols.emplace_back();
255+
}
212256

213-
size_t OldSize = ActiveCols.size();
214-
ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
215-
return OldSize;
257+
// Assign the element to the column and update the map.
258+
ElementToColumn[ElementIdx] = ColIdx;
259+
ActiveCols[ColIdx].ElementIdx = ElementIdx;
260+
return ColIdx;
261+
}
262+
263+
void LiveElementPrinter::freeColumn(unsigned ColIdx) {
264+
unsigned ElementIdx = ActiveCols[ColIdx].ElementIdx;
265+
266+
// Clear the column's data.
267+
ActiveCols[ColIdx].clear();
268+
269+
// Remove the element's entry from the map and add the column to the free
270+
// list.
271+
ElementToColumn.erase(ElementIdx);
272+
FreeCols.insert(ColIdx);
273+
}
274+
275+
std::vector<unsigned>
276+
LiveElementPrinter::getSortedActiveElementIndices() const {
277+
// Get all element indices that currently have an assigned column.
278+
std::vector<unsigned> Indices;
279+
for (const auto &Pair : ElementToColumn)
280+
Indices.push_back(Pair.first);
281+
282+
// Sort by the DWARF discovery order.
283+
llvm::stable_sort(Indices);
284+
return Indices;
216285
}
217286

218287
void LiveElementPrinter::dump() const {
@@ -239,57 +308,112 @@ void LiveElementPrinter::addCompileUnit(DWARFDie D) {
239308
void LiveElementPrinter::update(object::SectionedAddress ThisAddr,
240309
object::SectionedAddress NextAddr,
241310
bool IncludeDefinedVars) {
242-
// Do not create live ranges when debug-inlined-funcs option is provided with
243-
// line format option.
311+
// Exit early if only printing function limits.
244312
if (DbgInlinedFunctions == DFLimitsOnly)
245313
return;
246314

247-
// First, check variables which have already been assigned a column, so
248-
// that we don't change their order.
249-
SmallSet<unsigned, 8> CheckedElementIdxs;
315+
// Free columns identified in the previous cycle.
316+
for (unsigned ColIdx : ColumnsToFreeNextCycle)
317+
freeColumn(ColIdx);
318+
ColumnsToFreeNextCycle.clear();
319+
320+
// Update status of active columns and collect those to free next cycle.
250321
for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
251322
if (!ActiveCols[ColIdx].isActive())
252323
continue;
253324

254-
CheckedElementIdxs.insert(ActiveCols[ColIdx].ElementIdx);
255325
const std::unique_ptr<LiveElement> &LE =
256326
LiveElements[ActiveCols[ColIdx].ElementIdx];
257327
ActiveCols[ColIdx].LiveIn = LE->liveAtAddress(ThisAddr);
258328
ActiveCols[ColIdx].LiveOut = LE->liveAtAddress(NextAddr);
259-
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
260-
LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
261-
<< NextAddr.Address << ", " << Name << ", Col " << ColIdx
262-
<< ": LiveIn=" << ActiveCols[ColIdx].LiveIn
263-
<< ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
264329

265-
if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
330+
LLVM_DEBUG({
331+
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
332+
dbgs() << "pass 1, " << ThisAddr.Address << "-" << NextAddr.Address
333+
<< ", " << Name << ", Col " << ColIdx
334+
<< ": LiveIn=" << ActiveCols[ColIdx].LiveIn
335+
<< ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n";
336+
});
337+
338+
// If element is fully dead, deactivate column immediately.
339+
if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) {
266340
ActiveCols[ColIdx].ElementIdx = Column::NullElementIdx;
341+
continue;
342+
}
343+
344+
// Mark for cleanup in the next cycle if range ends here.
345+
if (ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
346+
ColumnsToFreeNextCycle.push_back(ColIdx);
267347
}
268348

269349
// Next, look for variables which don't already have a column, but which
270-
// are now live.
350+
// are now live (those starting at ThisAddr or NextAddr).
271351
if (IncludeDefinedVars) {
272-
for (unsigned ElementIdx = 0, End = LiveElements.size(); ElementIdx < End;
273-
++ElementIdx) {
274-
if (CheckedElementIdxs.count(ElementIdx))
352+
// Collect all elements starting at ThisAddr and NextAddr.
353+
std::vector<std::pair<unsigned, LiveElement *>> NewLiveElements;
354+
auto CollectNewElements = [&](const auto &It) {
355+
if (It == LiveElementsByAddress.end())
356+
return;
357+
358+
const std::vector<LiveElement *> &ElementList = It->second;
359+
for (LiveElement *LE : ElementList) {
360+
auto IndexIt = ElementPtrToIndex.find(LE);
361+
assert(IndexIt != ElementPtrToIndex.end() &&
362+
"LiveElement in address map but missing from index map!");
363+
364+
// Get the element index for sorting and column management.
365+
unsigned ElementIdx = IndexIt->second;
366+
// Skip elements that already have a column.
367+
if (ElementToColumn.count(ElementIdx))
368+
continue;
369+
370+
bool LiveIn = LE->liveAtAddress(ThisAddr);
371+
bool LiveOut = LE->liveAtAddress(NextAddr);
372+
if (!LiveIn && !LiveOut)
373+
continue;
374+
375+
NewLiveElements.emplace_back(ElementIdx, LE);
376+
}
377+
};
378+
379+
// Collect elements starting at ThisAddr.
380+
CollectNewElements(LiveElementsByAddress.find(ThisAddr.Address));
381+
// Collect elements starting at NextAddr (the address immediately
382+
// following the instruction).
383+
CollectNewElements(LiveElementsByAddress.find(NextAddr.Address));
384+
// Sort elements by DWARF discovery order for deterministic column
385+
// assignment.
386+
llvm::stable_sort(NewLiveElements, [](const auto &A, const auto &B) {
387+
return A.first < B.first;
388+
});
389+
390+
// Assign columns in deterministic order.
391+
for (const auto &ElementPair : NewLiveElements) {
392+
unsigned ElementIdx = ElementPair.first;
393+
// Skip if element was already added from the first range.
394+
if (ElementToColumn.count(ElementIdx))
275395
continue;
276396

277-
const std::unique_ptr<LiveElement> &LE = LiveElements[ElementIdx];
397+
LiveElement *LE = ElementPair.second;
278398
bool LiveIn = LE->liveAtAddress(ThisAddr);
279399
bool LiveOut = LE->liveAtAddress(NextAddr);
280-
if (!LiveIn && !LiveOut)
281-
continue;
282400

283-
unsigned ColIdx = findFreeColumn();
284-
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
285-
LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
286-
<< NextAddr.Address << ", " << Name << ", Col "
287-
<< ColIdx << ": LiveIn=" << LiveIn
288-
<< ", LiveOut=" << LiveOut << "\n");
289-
ActiveCols[ColIdx].ElementIdx = ElementIdx;
401+
// Assign or create a column.
402+
unsigned ColIdx = getOrCreateColumn(ElementIdx);
403+
LLVM_DEBUG({
404+
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
405+
dbgs() << "pass 2, " << ThisAddr.Address << "-" << NextAddr.Address
406+
<< ", " << Name << ", Col " << ColIdx << ": LiveIn=" << LiveIn
407+
<< ", LiveOut=" << LiveOut << "\n";
408+
});
409+
290410
ActiveCols[ColIdx].LiveIn = LiveIn;
291411
ActiveCols[ColIdx].LiveOut = LiveOut;
292412
ActiveCols[ColIdx].MustDrawLabel = true;
413+
414+
// Mark for cleanup next cycle if range ends here.
415+
if (ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
416+
ColumnsToFreeNextCycle.push_back(ColIdx);
293417
}
294418
}
295419
}
@@ -360,7 +484,13 @@ void LiveElementPrinter::printAfterOtherLine(formatted_raw_ostream &OS,
360484
void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS,
361485
bool MustPrint) {
362486
bool PrintedSomething = false;
363-
for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
487+
// Get all active elements, sorted by discovery order.
488+
std::vector<unsigned> SortedElementIndices = getSortedActiveElementIndices();
489+
// The outer loop iterates over the deterministic DWARF discovery order.
490+
for (unsigned ElementIdx : SortedElementIndices) {
491+
// Look up the physical column index (ColIdx) assigned to this
492+
// element. We use .at() because we are certain the element is active.
493+
unsigned ColIdx = ElementToColumn.at(ElementIdx);
364494
if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
365495
// First we need to print the live range markers for any active
366496
// columns to the left of this one.
@@ -375,8 +505,7 @@ void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS,
375505
OS << " ";
376506
}
377507

378-
const std::unique_ptr<LiveElement> &LE =
379-
LiveElements[ActiveCols[ColIdx].ElementIdx];
508+
const std::unique_ptr<LiveElement> &LE = LiveElements[ElementIdx];
380509
// Then print the variable name and location of the new live range,
381510
// with box drawing characters joining it to the live range line.
382511
OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive
@@ -438,22 +567,40 @@ void LiveElementPrinter::printAfterInst(formatted_raw_ostream &OS) {
438567
}
439568
}
440569

441-
void LiveElementPrinter::printStartLine(formatted_raw_ostream &OS,
442-
object::SectionedAddress Addr) {
443-
// Print a line to idenfity the start of an inlined function if line format
444-
// is specified.
445-
if (DbgInlinedFunctions == DFLimitsOnly)
446-
for (const std::unique_ptr<LiveElement> &LE : LiveElements)
447-
LE->printElementLine(OS, Addr, false);
448-
}
570+
void LiveElementPrinter::printBoundaryLine(formatted_raw_ostream &OS,
571+
object::SectionedAddress Addr,
572+
bool IsEnd) {
573+
// Only print the start/end line for inlined functions if DFLimitsOnly is
574+
// enabled.
575+
if (DbgInlinedFunctions != DFLimitsOnly)
576+
return;
449577

450-
void LiveElementPrinter::printEndLine(formatted_raw_ostream &OS,
451-
object::SectionedAddress Addr) {
452-
// Print a line to idenfity the end of an inlined function if line format is
453-
// specified.
454-
if (DbgInlinedFunctions == DFLimitsOnly)
455-
for (const std::unique_ptr<LiveElement> &LE : LiveElements)
456-
LE->printElementLine(OS, Addr, true);
578+
// Select the appropriate map based on whether we are checking the start
579+
// (LowPC) or end (HighPC) address.
580+
const auto &AddressMap =
581+
IsEnd ? LiveElementsByEndAddress : LiveElementsByAddress;
582+
583+
// Use the map to find all elements that start/end at the given address.
584+
std::vector<unsigned> ElementIndices;
585+
auto It = AddressMap.find(Addr.Address);
586+
if (It != AddressMap.end()) {
587+
for (LiveElement *LE : It->second) {
588+
// Look up the element index from the pointer.
589+
auto IndexIt = ElementPtrToIndex.find(LE);
590+
assert(IndexIt != ElementPtrToIndex.end() &&
591+
"LiveElement found in address map but missing index!");
592+
ElementIndices.push_back(IndexIt->second);
593+
}
594+
}
595+
596+
// Sort the indices to ensure deterministic output order (by DWARF discovery
597+
// order).
598+
llvm::stable_sort(ElementIndices);
599+
600+
for (unsigned ElementIdx : ElementIndices) {
601+
LiveElement *LE = LiveElements[ElementIdx].get();
602+
LE->printElementLine(OS, Addr, IsEnd);
603+
}
457604
}
458605

459606
bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {

0 commit comments

Comments
 (0)