[llvm] [BOLT][DWARF] Refactor address ranges processing (PR #71225)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 3 13:20:17 PDT 2023
https://github.com/maksfb created https://github.com/llvm/llvm-project/pull/71225
Create BinaryFunction::translateInputToOutputRange() and use it for updating DWARF debug ranges and location lists while de-duplicating the existing code. Additionally, move DWARF-specific code out of BinaryFunction and add print functions to facilitate debugging.
Note that this change is deliberately kept "bug-level" compatible with the existing solution to keep it NFCI and make it easier to track any possible regressions in the future updates to the ranges-handling code.
>From c85b4c9ef261f0854195a0d4997b71d5d84a7d13 Mon Sep 17 00:00:00 2001
From: Maksim Panchenko <maks at fb.com>
Date: Tue, 31 Oct 2023 11:26:08 -0700
Subject: [PATCH] [BOLT][DWARF] Refactor address ranges processing
Create BinaryFunction::translateInputToOutputRange() and use it for
updating DWARF debug ranges and location lists while de-duplicating the
existing code. Additionally, move DWARF-specific code out of
BinaryFunction and add print functions to facilitate debugging.
Note that this change is deliberately kept "bug-level" compatible with
the existing solution to keep it NFCI and make it easier to track any
possible regressions in the future updates to the ranges-handling code.
---
bolt/include/bolt/Core/BinaryFunction.h | 13 +-
bolt/include/bolt/Core/DebugData.h | 19 ++
bolt/lib/Core/BinaryFunction.cpp | 224 +++++++-----------------
bolt/lib/Rewrite/DWARFRewriter.cpp | 94 +++++++++-
4 files changed, 182 insertions(+), 168 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index c67ddccbf4892a7..003de6fd2bfa8f2 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -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;
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index 3fdc0534c000ae9..a2776c1e5ae884e 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -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>
@@ -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>;
@@ -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
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 2e1fa739e53f20b..973a45877a7003f 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -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) {
+ 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) {
@@ -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;
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 0ac8e3956b90f61..3d561e86812d131 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -88,6 +88,96 @@ static void printDie(DWARFUnit &DU, uint64_t DIEOffset) {
}
}
+using namespace bolt;
+
+/// 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
+translateInputToOutputRanges(const BinaryFunction &BF,
+ const DWARFAddressRangesVector &InputRanges) {
+ DebugAddressRangesVector OutputRanges;
+
+ // If the function hasn't changed return the same ranges.
+ if (!BF.isEmitted()) {
+ OutputRanges.resize(InputRanges.size());
+ llvm::transform(InputRanges, OutputRanges.begin(),
+ [](const DWARFAddressRange &Range) {
+ return DebugAddressRange(Range.LowPC, Range.HighPC);
+ });
+ return OutputRanges;
+ }
+
+ for (const DWARFAddressRange &Range : InputRanges)
+ llvm::append_range(OutputRanges, BF.translateInputToOutputRange(
+ {Range.LowPC, Range.HighPC}));
+
+ // Post-processing pass to sort and merge ranges.
+ llvm::sort(OutputRanges);
+ DebugAddressRangesVector MergedRanges;
+ uint64_t PrevHighPC = 0;
+ for (const DebugAddressRange &Range : OutputRanges) {
+ if (Range.LowPC <= PrevHighPC) {
+ MergedRanges.back().HighPC =
+ std::max(MergedRanges.back().HighPC, Range.HighPC);
+ } else {
+ MergedRanges.emplace_back(Range.LowPC, Range.HighPC);
+ }
+ PrevHighPC = MergedRanges.back().HighPC;
+ }
+
+ return MergedRanges;
+}
+
+/// Similar to translateInputToOutputRanges() but operates on location lists.
+static DebugLocationsVector
+translateInputToOutputLocationList(const BinaryFunction &BF,
+ const DebugLocationsVector &InputLL) {
+ DebugLocationsVector OutputLL;
+
+ // If the function hasn't changed - there's nothing to update.
+ if (!BF.isEmitted())
+ return InputLL;
+
+ for (const DebugLocationEntry &Entry : InputLL) {
+ DebugAddressRangesVector OutRanges =
+ BF.translateInputToOutputRange({Entry.LowPC, Entry.HighPC});
+ if (!OutRanges.empty() && !OutputLL.empty()) {
+ if (OutRanges.front().LowPC == OutputLL.back().HighPC &&
+ Entry.Expr == OutputLL.back().Expr) {
+ OutputLL.back().HighPC =
+ std::max(OutputLL.back().HighPC, OutRanges.front().HighPC);
+ OutRanges.erase(OutRanges.begin());
+ }
+ }
+ llvm::transform(OutRanges, std::back_inserter(OutputLL),
+ [&Entry](const DebugAddressRange &R) {
+ return DebugLocationEntry{R.LowPC, R.HighPC, Entry.Expr};
+ });
+ }
+
+ // Sort and merge adjacent entries with identical locations.
+ llvm::stable_sort(
+ OutputLL, [](const DebugLocationEntry &A, const DebugLocationEntry &B) {
+ return A.LowPC < B.LowPC;
+ });
+ DebugLocationsVector MergedLL;
+ uint64_t PrevHighPC = 0;
+ const SmallVectorImpl<uint8_t> *PrevExpr = nullptr;
+ for (const DebugLocationEntry &Entry : OutputLL) {
+ if (Entry.LowPC <= PrevHighPC && *PrevExpr == Entry.Expr) {
+ MergedLL.back().HighPC = std::max(Entry.HighPC, MergedLL.back().HighPC);
+ } else {
+ const uint64_t Begin = std::max(Entry.LowPC, PrevHighPC);
+ const uint64_t End = std::max(Begin, Entry.HighPC);
+ MergedLL.emplace_back(DebugLocationEntry{Begin, End, Entry.Expr});
+ }
+ PrevHighPC = MergedLL.back().HighPC;
+ PrevExpr = &MergedLL.back().Expr;
+ }
+
+ return MergedLL;
+}
+
namespace llvm {
namespace bolt {
/// Emits debug information into .debug_info or .debug_types section.
@@ -861,7 +951,7 @@ void DWARFRewriter::updateUnitDebugInfo(
: nullptr;
DebugAddressRangesVector OutputRanges;
if (Function) {
- OutputRanges = Function->translateInputToOutputRanges(*RangesOrError);
+ OutputRanges = translateInputToOutputRanges(*Function, *RangesOrError);
LLVM_DEBUG(if (OutputRanges.empty() != RangesOrError->empty()) {
dbgs() << "BOLT-DEBUG: problem with DIE at 0x"
<< Twine::utohexstr(Die->getOffset()) << " in CU at 0x"
@@ -1022,7 +1112,7 @@ void DWARFRewriter::updateUnitDebugInfo(
DebugLocationsVector OutputLL;
if (const BinaryFunction *Function =
BC.getBinaryFunctionContainingAddress(Address)) {
- OutputLL = Function->translateInputToOutputLocationList(InputLL);
+ OutputLL = translateInputToOutputLocationList(*Function, InputLL);
LLVM_DEBUG(if (OutputLL.empty()) {
dbgs() << "BOLT-DEBUG: location list translated to an empty "
"one at 0x"
More information about the llvm-commits
mailing list