Skip to content

[BOLT][DWARF] Refactor address ranges processing #71225

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions bolt/include/bolt/Core/BinaryFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2309,15 +2309,10 @@ class BinaryFunction {
/// removed.
uint64_t translateInputToOutputAddress(uint64_t Address) const;

/// Take address ranges corresponding to the input binary and translate
/// them to address ranges in the output binary.
DebugAddressRangesVector translateInputToOutputRanges(
const DWARFAddressRangesVector &InputRanges) const;

/// Similar to translateInputToOutputRanges() but operates on location lists
/// and moves associated data to output location lists.
DebugLocationsVector
translateInputToOutputLocationList(const DebugLocationsVector &InputLL) const;
/// Translate a contiguous range of addresses in the input binary into a set
/// of ranges in the output binary.
DebugAddressRangesVector
translateInputToOutputRange(DebugAddressRange InRange) const;

/// Return true if the function is an AArch64 linker inserted veneer
bool isAArch64Veneer() const;
Expand Down
19 changes: 19 additions & 0 deletions bolt/include/bolt/Core/DebugData.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "llvm/CodeGen/DIE.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCDwarf.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdint>
Expand Down Expand Up @@ -95,6 +96,12 @@ static inline bool operator<(const DebugAddressRange &LHS,
return std::tie(LHS.LowPC, LHS.HighPC) < std::tie(RHS.LowPC, RHS.HighPC);
}

inline raw_ostream &operator<<(raw_ostream &OS,
const DebugAddressRange &Range) {
OS << formatv("[{0:x}, {1:x})", Range.LowPC, Range.HighPC);
return OS;
}

/// DebugAddressRangesVector - represents a set of absolute address ranges.
using DebugAddressRangesVector = SmallVector<DebugAddressRange, 2>;

Expand All @@ -106,6 +113,18 @@ struct DebugLocationEntry {
SmallVector<uint8_t, 4> Expr;
};

inline raw_ostream &operator<<(raw_ostream &OS,
const DebugLocationEntry &Entry) {
OS << formatv("[{0:x}, {1:x}) : [", Entry.LowPC, Entry.HighPC);
const char *Sep = "";
for (unsigned Byte : Entry.Expr) {
OS << Sep << Byte;
Sep = ", ";
}
OS << "]";
return OS;
}

using DebugLocationsVector = SmallVector<DebugLocationEntry, 4>;

/// References a row in a DWARFDebugLine::LineTable by the DWARF
Expand Down
224 changes: 67 additions & 157 deletions bolt/lib/Core/BinaryFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4251,92 +4251,88 @@ uint64_t BinaryFunction::translateInputToOutputAddress(uint64_t Address) const {
BB->getOutputAddressRange().second);
}

DebugAddressRangesVector BinaryFunction::translateInputToOutputRanges(
const DWARFAddressRangesVector &InputRanges) const {
DebugAddressRangesVector OutputRanges;
DebugAddressRangesVector
BinaryFunction::translateInputToOutputRange(DebugAddressRange InRange) const {
DebugAddressRangesVector OutRanges;

// The function was removed from the output. Return an empty range.
if (isFolded())
return OutputRanges;
return OutRanges;

// If the function hasn't changed return the same ranges.
// If the function hasn't changed return the same range.
if (!isEmitted()) {
OutputRanges.resize(InputRanges.size());
llvm::transform(InputRanges, OutputRanges.begin(),
[](const DWARFAddressRange &Range) {
return DebugAddressRange(Range.LowPC, Range.HighPC);
});
return OutputRanges;
OutRanges.emplace_back(InRange);
return OutRanges;
}

if (!containsAddress(InRange.LowPC))
return OutRanges;

// Special case of an empty range [X, X). Some tools expect X to be updated.
if (InRange.LowPC == InRange.HighPC) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens here when lowPC is tombstoned to 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will return an empty range. Same as before. For more context: this function is not supposed to handle special values. E.g. all DWARF-specific processing should happen at a higher level.

if (uint64_t NewPC = translateInputToOutputAddress(InRange.LowPC))
OutRanges.push_back(DebugAddressRange{NewPC, NewPC});
return OutRanges;
}

// Even though we will merge ranges in a post-processing pass, we attempt to
// merge them in a main processing loop as it improves the processing time.
uint64_t PrevEndAddress = 0;
for (const DWARFAddressRange &Range : InputRanges) {
if (!containsAddress(Range.LowPC)) {
uint64_t InputOffset = InRange.LowPC - getAddress();
const uint64_t InputEndOffset =
std::min(InRange.HighPC - getAddress(), getSize());

auto BBI = llvm::upper_bound(BasicBlockOffsets,
BasicBlockOffset(InputOffset, nullptr),
CompareBasicBlockOffsets());
assert(BBI != BasicBlockOffsets.begin());

// Iterate over blocks in the input order using BasicBlockOffsets.
for (--BBI; InputOffset < InputEndOffset && BBI != BasicBlockOffsets.end();
InputOffset = BBI->second->getEndOffset(), ++BBI) {
const BinaryBasicBlock &BB = *BBI->second;
if (InputOffset < BB.getOffset() || InputOffset >= BB.getEndOffset()) {
LLVM_DEBUG(
dbgs() << "BOLT-DEBUG: invalid debug address range detected for "
<< *this << " : [0x" << Twine::utohexstr(Range.LowPC) << ", 0x"
<< Twine::utohexstr(Range.HighPC) << "]\n");
PrevEndAddress = 0;
continue;
<< *this << " : [0x" << Twine::utohexstr(InRange.LowPC)
<< ", 0x" << Twine::utohexstr(InRange.HighPC) << "]\n");
break;
}
uint64_t InputOffset = Range.LowPC - getAddress();
const uint64_t InputEndOffset =
std::min(Range.HighPC - getAddress(), getSize());

auto BBI = llvm::upper_bound(BasicBlockOffsets,
BasicBlockOffset(InputOffset, nullptr),
CompareBasicBlockOffsets());
--BBI;
do {
const BinaryBasicBlock *BB = BBI->second;
if (InputOffset < BB->getOffset() || InputOffset >= BB->getEndOffset()) {
LLVM_DEBUG(
dbgs() << "BOLT-DEBUG: invalid debug address range detected for "
<< *this << " : [0x" << Twine::utohexstr(Range.LowPC)
<< ", 0x" << Twine::utohexstr(Range.HighPC) << "]\n");
PrevEndAddress = 0;
break;
}

// Skip the range if the block was deleted.
if (const uint64_t OutputStart = BB->getOutputAddressRange().first) {
const uint64_t StartAddress =
OutputStart + InputOffset - BB->getOffset();
uint64_t EndAddress = BB->getOutputAddressRange().second;
if (InputEndOffset < BB->getEndOffset())
EndAddress = StartAddress + InputEndOffset - InputOffset;

if (StartAddress == PrevEndAddress) {
OutputRanges.back().HighPC =
std::max(OutputRanges.back().HighPC, EndAddress);
} else {
OutputRanges.emplace_back(StartAddress,
std::max(StartAddress, EndAddress));
}
PrevEndAddress = OutputRanges.back().HighPC;
}
// Skip the block if it wasn't emitted.
if (!BB.getOutputAddressRange().first)
continue;

InputOffset = BB->getEndOffset();
++BBI;
} while (InputOffset < InputEndOffset);
}
// Find output address for an instruction with an offset greater or equal
// to /p Offset. The output address should fall within the same basic
// block boundaries.
auto translateBlockOffset = [&](const uint64_t Offset) {
const uint64_t OutAddress = BB.getOutputAddressRange().first + Offset;
return OutAddress;
};

// Post-processing pass to sort and merge ranges.
llvm::sort(OutputRanges);
DebugAddressRangesVector MergedRanges;
PrevEndAddress = 0;
for (const DebugAddressRange &Range : OutputRanges) {
if (Range.LowPC <= PrevEndAddress) {
MergedRanges.back().HighPC =
std::max(MergedRanges.back().HighPC, Range.HighPC);
} else {
MergedRanges.emplace_back(Range.LowPC, Range.HighPC);
uint64_t OutLowPC = BB.getOutputAddressRange().first;
if (InputOffset > BB.getOffset())
OutLowPC = translateBlockOffset(InputOffset - BB.getOffset());

uint64_t OutHighPC = BB.getOutputAddressRange().second;
if (InputEndOffset < BB.getEndOffset()) {
assert(InputEndOffset >= BB.getOffset());
OutHighPC = translateBlockOffset(InputEndOffset - BB.getOffset());
}
PrevEndAddress = MergedRanges.back().HighPC;

// Check if we can expand the last translated range.
if (!OutRanges.empty() && OutRanges.back().HighPC == OutLowPC)
OutRanges.back().HighPC = std::max(OutRanges.back().HighPC, OutHighPC);
else
OutRanges.emplace_back(OutLowPC, std::max(OutLowPC, OutHighPC));
}

return MergedRanges;
LLVM_DEBUG({
dbgs() << "BOLT-DEBUG: translated address range " << InRange << " -> ";
for (const DebugAddressRange &R : OutRanges)
dbgs() << R << ' ';
dbgs() << '\n';
});

return OutRanges;
}

MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) {
Expand Down Expand Up @@ -4367,92 +4363,6 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) {
}
}

DebugLocationsVector BinaryFunction::translateInputToOutputLocationList(
const DebugLocationsVector &InputLL) const {
DebugLocationsVector OutputLL;

if (isFolded())
return OutputLL;

// If the function hasn't changed - there's nothing to update.
if (!isEmitted())
return InputLL;

uint64_t PrevEndAddress = 0;
SmallVectorImpl<uint8_t> *PrevExpr = nullptr;
for (const DebugLocationEntry &Entry : InputLL) {
const uint64_t Start = Entry.LowPC;
const uint64_t End = Entry.HighPC;
if (!containsAddress(Start)) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invalid debug address range detected "
"for "
<< *this << " : [0x" << Twine::utohexstr(Start)
<< ", 0x" << Twine::utohexstr(End) << "]\n");
continue;
}
uint64_t InputOffset = Start - getAddress();
const uint64_t InputEndOffset = std::min(End - getAddress(), getSize());
auto BBI = llvm::upper_bound(BasicBlockOffsets,
BasicBlockOffset(InputOffset, nullptr),
CompareBasicBlockOffsets());
--BBI;
do {
const BinaryBasicBlock *BB = BBI->second;
if (InputOffset < BB->getOffset() || InputOffset >= BB->getEndOffset()) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invalid debug address range detected "
"for "
<< *this << " : [0x" << Twine::utohexstr(Start)
<< ", 0x" << Twine::utohexstr(End) << "]\n");
PrevEndAddress = 0;
break;
}

// Skip the range if the block was deleted.
if (const uint64_t OutputStart = BB->getOutputAddressRange().first) {
const uint64_t StartAddress =
OutputStart + InputOffset - BB->getOffset();
uint64_t EndAddress = BB->getOutputAddressRange().second;
if (InputEndOffset < BB->getEndOffset())
EndAddress = StartAddress + InputEndOffset - InputOffset;

if (StartAddress == PrevEndAddress && Entry.Expr == *PrevExpr) {
OutputLL.back().HighPC = std::max(OutputLL.back().HighPC, EndAddress);
} else {
OutputLL.emplace_back(DebugLocationEntry{
StartAddress, std::max(StartAddress, EndAddress), Entry.Expr});
}
PrevEndAddress = OutputLL.back().HighPC;
PrevExpr = &OutputLL.back().Expr;
}

++BBI;
InputOffset = BB->getEndOffset();
} while (InputOffset < InputEndOffset);
}

// Sort and merge adjacent entries with identical location.
llvm::stable_sort(
OutputLL, [](const DebugLocationEntry &A, const DebugLocationEntry &B) {
return A.LowPC < B.LowPC;
});
DebugLocationsVector MergedLL;
PrevEndAddress = 0;
PrevExpr = nullptr;
for (const DebugLocationEntry &Entry : OutputLL) {
if (Entry.LowPC <= PrevEndAddress && *PrevExpr == Entry.Expr) {
MergedLL.back().HighPC = std::max(Entry.HighPC, MergedLL.back().HighPC);
} else {
const uint64_t Begin = std::max(Entry.LowPC, PrevEndAddress);
const uint64_t End = std::max(Begin, Entry.HighPC);
MergedLL.emplace_back(DebugLocationEntry{Begin, End, Entry.Expr});
}
PrevEndAddress = MergedLL.back().HighPC;
PrevExpr = &MergedLL.back().Expr;
}

return MergedLL;
}

void BinaryFunction::printLoopInfo(raw_ostream &OS) const {
if (!opts::shouldPrint(*this))
return;
Expand Down
Loading