[llvm] [BOLT] Fix debug line emission for functions in multiple compilation units (PR #151230)
Grigory Pastukhov via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 11 09:55:33 PDT 2025
https://github.com/grigorypas updated https://github.com/llvm/llvm-project/pull/151230
>From e95394dcf20b0c759421daa148e28b28efc30f9c Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Thu, 24 Jul 2025 13:50:13 -0700
Subject: [PATCH 01/12] Change DwarfUnit field to vector in BinaryFunction
---
bolt/include/bolt/Core/BinaryFunction.h | 27 ++++++---
bolt/lib/Core/BinaryContext.cpp | 38 +++++++-----
bolt/lib/Core/BinaryEmitter.cpp | 80 ++++++++++++++-----------
bolt/lib/Core/BinaryFunction.cpp | 13 +++-
4 files changed, 95 insertions(+), 63 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index b59926cc75571..11d7d0f9765c4 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -423,8 +423,8 @@ class BinaryFunction {
/// Original LSDA type encoding
unsigned LSDATypeEncoding{dwarf::DW_EH_PE_omit};
- /// Containing compilation unit for the function.
- DWARFUnit *DwarfUnit{nullptr};
+ /// All compilation units this function belongs to.
+ SmallVector<DWARFUnit *, 1> DwarfUnitVec;
/// Last computed hash value. Note that the value could be recomputed using
/// different parameters by every pass.
@@ -2409,15 +2409,24 @@ class BinaryFunction {
void
computeBlockHashes(HashFunction HashFunction = HashFunction::Default) const;
- void setDWARFUnit(DWARFUnit *Unit) { DwarfUnit = Unit; }
+ void addDWARFUnit(DWARFUnit *Unit) { DwarfUnitVec.push_back(Unit); }
- /// Return DWARF compile unit for this function.
- DWARFUnit *getDWARFUnit() const { return DwarfUnit; }
+ void removeDWARFUnit(DWARFUnit *Unit) {
+ auto *It = std::find(DwarfUnitVec.begin(), DwarfUnitVec.end(), Unit);
+ // If found, erase it
+ if (It != DwarfUnitVec.end()) {
+ DwarfUnitVec.erase(It);
+ }
+ }
+
+ /// Return DWARF compile units for this function.
+ const SmallVector<DWARFUnit *, 1> getDWARFUnits() const {
+ return DwarfUnitVec;
+ }
- /// Return line info table for this function.
- const DWARFDebugLine::LineTable *getDWARFLineTable() const {
- return getDWARFUnit() ? BC.DwCtx->getLineTableForUnit(getDWARFUnit())
- : nullptr;
+ const DWARFDebugLine::LineTable *
+ getDWARFLineTableForUnit(DWARFUnit *Unit) const {
+ return BC.DwCtx->getLineTableForUnit(Unit);
}
/// Finalize profile for the function.
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index dd0d041692484..320e38682bdba 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1693,22 +1693,35 @@ void BinaryContext::preprocessDebugInfo() {
auto It = llvm::partition_point(
AllRanges, [=](CURange R) { return R.HighPC <= FunctionAddress; });
- if (It != AllRanges.end() && It->LowPC <= FunctionAddress)
- Function.setDWARFUnit(It->Unit);
+ if (It == AllRanges.end() || It->LowPC > FunctionAddress) {
+ continue;
+ }
+ Function.addDWARFUnit(It->Unit);
+
+ // Go forward and add all units from ranges that cover the function
+ while (++It != AllRanges.end()) {
+ if (It->LowPC <= FunctionAddress && FunctionAddress < It->HighPC) {
+ Function.addDWARFUnit(It->Unit);
+ } else {
+ break;
+ }
+ }
}
// Discover units with debug info that needs to be updated.
for (const auto &KV : BinaryFunctions) {
const BinaryFunction &BF = KV.second;
- if (shouldEmit(BF) && BF.getDWARFUnit())
- ProcessedCUs.insert(BF.getDWARFUnit());
+ if (shouldEmit(BF) && !BF.getDWARFUnits().empty())
+ for (const DWARFUnit *Unit : BF.getDWARFUnits())
+ ProcessedCUs.insert(Unit);
}
-
// Clear debug info for functions from units that we are not going to process.
for (auto &KV : BinaryFunctions) {
BinaryFunction &BF = KV.second;
- if (BF.getDWARFUnit() && !ProcessedCUs.count(BF.getDWARFUnit()))
- BF.setDWARFUnit(nullptr);
+ for (auto *Unit : BF.getDWARFUnits()) {
+ if (!ProcessedCUs.count(Unit))
+ BF.removeDWARFUnit(Unit);
+ }
}
if (opts::Verbosity >= 1) {
@@ -1908,14 +1921,9 @@ static void printDebugInfo(raw_ostream &OS, const MCInst &Instruction,
if (RowRef == DebugLineTableRowRef::NULL_ROW)
return;
- const DWARFDebugLine::LineTable *LineTable;
- if (Function && Function->getDWARFUnit() &&
- Function->getDWARFUnit()->getOffset() == RowRef.DwCompileUnitIndex) {
- LineTable = Function->getDWARFLineTable();
- } else {
- LineTable = DwCtx->getLineTableForUnit(
- DwCtx->getCompileUnitForOffset(RowRef.DwCompileUnitIndex));
- }
+ const DWARFDebugLine::LineTable *LineTable = DwCtx->getLineTableForUnit(
+ DwCtx->getCompileUnitForOffset(RowRef.DwCompileUnitIndex));
+
assert(LineTable && "line table expected for instruction with debug info");
const DWARFDebugLine::Row &Row = LineTable->Rows[RowRef.RowIndex - 1];
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index 7b5cd276fee89..34bda7403d259 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -177,7 +177,8 @@ class BinaryEmitter {
/// Note that it does not automatically result in the insertion of the EOS
/// marker in the line table program, but provides one to the DWARF generator
/// when it needs it.
- void emitLineInfoEnd(const BinaryFunction &BF, MCSymbol *FunctionEndSymbol);
+ void emitLineInfoEnd(const BinaryFunction &BF, MCSymbol *FunctionEndSymbol,
+ DWARFUnit *Unit);
/// Emit debug line info for unprocessed functions from CUs that include
/// emitted functions.
@@ -436,8 +437,9 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function,
Streamer.emitELFSize(StartSymbol, SizeExpr);
}
- if (opts::UpdateDebugSections && Function.getDWARFUnit())
- emitLineInfoEnd(Function, EndSymbol);
+ // TODO: Emit line info end for all the CUs that contain the function.
+ if (opts::UpdateDebugSections && !Function.getDWARFUnits().empty())
+ emitLineInfoEnd(Function, EndSymbol, Function.getDWARFUnits().front());
// Exception handling info for the function.
emitLSDA(Function, FF);
@@ -486,7 +488,7 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
// A symbol to be emitted before the instruction to mark its location.
MCSymbol *InstrLabel = BC.MIB->getInstLabel(Instr);
- if (opts::UpdateDebugSections && BF.getDWARFUnit()) {
+ if (opts::UpdateDebugSections && !BF.getDWARFUnits().empty()) {
LastLocSeen = emitLineInfo(BF, Instr.getLoc(), LastLocSeen,
FirstInstr, InstrLabel);
FirstInstr = false;
@@ -679,8 +681,10 @@ void BinaryEmitter::emitConstantIslands(BinaryFunction &BF, bool EmitColdPart,
SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
SMLoc PrevLoc, bool FirstInstr,
MCSymbol *&InstrLabel) {
- DWARFUnit *FunctionCU = BF.getDWARFUnit();
- const DWARFDebugLine::LineTable *FunctionLineTable = BF.getDWARFLineTable();
+ // TODO: implment emitting into line tables corresponding to multiple CUs
+ DWARFUnit *FunctionCU = BF.getDWARFUnits().front();
+ const DWARFDebugLine::LineTable *FunctionLineTable =
+ BF.getDWARFLineTableForUnit(FunctionCU);
assert(FunctionCU && "cannot emit line info for function without CU");
DebugLineTableRowRef RowReference = DebugLineTableRowRef::fromSMLoc(NewLoc);
@@ -740,13 +744,13 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
}
void BinaryEmitter::emitLineInfoEnd(const BinaryFunction &BF,
- MCSymbol *FunctionEndLabel) {
- DWARFUnit *FunctionCU = BF.getDWARFUnit();
- assert(FunctionCU && "DWARF unit expected");
+ MCSymbol *FunctionEndLabel,
+ DWARFUnit *Unit) {
+ assert(Unit && "DWARF unit expected");
BC.Ctx->setCurrentDwarfLoc(0, 0, 0, DWARF2_FLAG_END_SEQUENCE, 0, 0);
const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc();
BC.Ctx->clearDwarfLocSeen();
- BC.getDwarfLineTable(FunctionCU->getOffset())
+ BC.getDwarfLineTable(Unit->getOffset())
.getMCLineSections()
.addLineEntry(MCDwarfLineEntry(FunctionEndLabel, DwarfLoc),
Streamer.getCurrentSectionOnly());
@@ -1115,36 +1119,40 @@ void BinaryEmitter::emitDebugLineInfoForOriginalFunctions() {
if (Function.isEmitted())
continue;
- const DWARFDebugLine::LineTable *LineTable = Function.getDWARFLineTable();
- if (!LineTable)
- continue; // nothing to update for this function
+ // Loop through all CUs in the function
+ for (DWARFUnit *Unit : Function.getDWARFUnits()) {
+ const DWARFDebugLine::LineTable *LineTable =
+ Function.getDWARFLineTableForUnit(Unit);
+ if (!LineTable)
+ continue; // nothing to update for this unit
+
+ const uint64_t Address = Function.getAddress();
+ std::vector<uint32_t> Results;
+ if (!LineTable->lookupAddressRange(
+ {Address, object::SectionedAddress::UndefSection},
+ Function.getSize(), Results))
+ continue;
- const uint64_t Address = Function.getAddress();
- std::vector<uint32_t> Results;
- if (!LineTable->lookupAddressRange(
- {Address, object::SectionedAddress::UndefSection},
- Function.getSize(), Results))
- continue;
+ if (Results.empty())
+ continue;
- if (Results.empty())
- continue;
+ // The first row returned could be the last row matching the start
+ // address. Find the first row with the same address that is not the end
+ // of the sequence.
+ uint64_t FirstRow = Results.front();
+ while (FirstRow > 0) {
+ const DWARFDebugLine::Row &PrevRow = LineTable->Rows[FirstRow - 1];
+ if (PrevRow.Address.Address != Address || PrevRow.EndSequence)
+ break;
+ --FirstRow;
+ }
- // The first row returned could be the last row matching the start address.
- // Find the first row with the same address that is not the end of the
- // sequence.
- uint64_t FirstRow = Results.front();
- while (FirstRow > 0) {
- const DWARFDebugLine::Row &PrevRow = LineTable->Rows[FirstRow - 1];
- if (PrevRow.Address.Address != Address || PrevRow.EndSequence)
- break;
- --FirstRow;
+ const uint64_t EndOfSequenceAddress =
+ Function.getAddress() + Function.getMaxSize();
+ BC.getDwarfLineTable(Unit->getOffset())
+ .addLineTableSequence(LineTable, FirstRow, Results.back(),
+ EndOfSequenceAddress);
}
-
- const uint64_t EndOfSequenceAddress =
- Function.getAddress() + Function.getMaxSize();
- BC.getDwarfLineTable(Function.getDWARFUnit()->getOffset())
- .addLineTableSequence(LineTable, FirstRow, Results.back(),
- EndOfSequenceAddress);
}
// For units that are completely unprocessed, use original debug line contents
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 6cac2d0cca2cb..72302c4170e64 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1496,9 +1496,16 @@ Error BinaryFunction::disassemble() {
}
add_instruction:
- if (getDWARFLineTable()) {
- Instruction.setLoc(findDebugLineInformationForInstructionAt(
- AbsoluteInstrAddr, getDWARFUnit(), getDWARFLineTable()));
+ // TODO: Handle multiple DWARF compilation units properly.
+ // For now, use the first unit if available.
+ if (!getDWARFUnits().empty()) {
+ DWARFUnit *FirstUnit = getDWARFUnits().front();
+ const DWARFDebugLine::LineTable *LineTable =
+ getDWARFLineTableForUnit(FirstUnit);
+ if (LineTable) {
+ Instruction.setLoc(findDebugLineInformationForInstructionAt(
+ AbsoluteInstrAddr, FirstUnit, LineTable));
+ }
}
// Record offset of the instruction for profile matching.
>From 53146d8c036a5842779d12b5b24576d7acc4a632 Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Thu, 24 Jul 2025 20:30:10 -0700
Subject: [PATCH 02/12] Implemented multiple rows per instruction logic
---
bolt/include/bolt/Core/BinaryContext.h | 6 +
bolt/include/bolt/Core/DebugData.h | 116 +++++++++++++----
bolt/lib/Core/BinaryContext.cpp | 13 +-
bolt/lib/Core/BinaryEmitter.cpp | 136 ++++++++++++--------
bolt/lib/Core/BinaryFunction.cpp | 58 ++++-----
bolt/lib/Core/DebugData.cpp | 2 -
bolt/test/Inputs/multi-cu-common.h | 10 ++
bolt/test/Inputs/multi-cu-file1.c | 9 ++
bolt/test/Inputs/multi-cu-file2.c | 8 ++
bolt/test/Inputs/process-debug-line.sh | 101 +++++++++++++++
bolt/test/X86/multi-cu-debug-line.test | 108 ++++++++++++++++
bolt/test/perf2bolt/Inputs/perf_test.lds | 11 +-
bolt/unittests/Core/CMakeLists.txt | 1 +
bolt/unittests/Core/ClusteredRows.cpp | 152 +++++++++++++++++++++++
14 files changed, 615 insertions(+), 116 deletions(-)
create mode 100644 bolt/test/Inputs/multi-cu-common.h
create mode 100644 bolt/test/Inputs/multi-cu-file1.c
create mode 100644 bolt/test/Inputs/multi-cu-file2.c
create mode 100755 bolt/test/Inputs/process-debug-line.sh
create mode 100644 bolt/test/X86/multi-cu-debug-line.test
create mode 100644 bolt/unittests/Core/ClusteredRows.cpp
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 91ecf89da618c..48bc9a5d1f92c 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -288,6 +288,12 @@ class BinaryContext {
/// overwritten, but it is okay to re-generate debug info for them.
std::set<const DWARFUnit *> ProcessedCUs;
+ /// DWARF-related container to manage lifecycle of groups of rows from line
+ /// tables associated with instructions. Since binary functions can span
+ /// multiple compilation units, instructions may reference debug line
+ /// information from multiple CUs.
+ ClasteredRowsContainer ClasteredRows;
+
// Setup MCPlus target builder
void initializeTarget(std::unique_ptr<MCPlusBuilder> TargetBuilder) {
MIB = std::move(TargetBuilder);
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index 6ea3b1af1024f..048594946d8a9 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -135,8 +135,6 @@ struct DebugLineTableRowRef {
uint32_t DwCompileUnitIndex;
uint32_t RowIndex;
- const static DebugLineTableRowRef NULL_ROW;
-
bool operator==(const DebugLineTableRowRef &Rhs) const {
return DwCompileUnitIndex == Rhs.DwCompileUnitIndex &&
RowIndex == Rhs.RowIndex;
@@ -145,24 +143,6 @@ struct DebugLineTableRowRef {
bool operator!=(const DebugLineTableRowRef &Rhs) const {
return !(*this == Rhs);
}
-
- static DebugLineTableRowRef fromSMLoc(const SMLoc &Loc) {
- union {
- decltype(Loc.getPointer()) Ptr;
- DebugLineTableRowRef Ref;
- } U;
- U.Ptr = Loc.getPointer();
- return U.Ref;
- }
-
- SMLoc toSMLoc() const {
- union {
- decltype(SMLoc().getPointer()) Ptr;
- DebugLineTableRowRef Ref;
- } U;
- U.Ref = *this;
- return SMLoc::getFromPointer(U.Ptr);
- }
};
/// Common buffer vector used for debug info handling.
@@ -210,7 +190,7 @@ class DebugRangesSectionWriter {
static bool classof(const DebugRangesSectionWriter *Writer) {
return Writer->getKind() == RangesWriterKind::DebugRangesWriter;
}
-
+
/// Append a range to the main buffer.
void appendToRangeBuffer(const DebugBufferVector &CUBuffer);
@@ -852,6 +832,100 @@ class DwarfLineTable {
// Returns DWARF Version for this line table.
uint16_t getDwarfVersion() const { return DwarfVersion; }
};
+
+/// ClusteredRows represents a collection of debug line table row references.
+/// Since a Binary function can belong to multiple compilation units (CUs),
+/// a single MCInst can have multiple debug line table rows associated with it
+/// from different CUs. This class manages such clustered row references.
+///
+/// MEMORY LAYOUT AND DESIGN:
+/// This class uses a flexible array member pattern to store all
+/// DebugLineTableRowRef elements in a single contiguous memory allocation.
+/// The memory layout is:
+///
+/// +------------------+
+/// | ClusteredRows | <- Object header (Size + first element)
+/// | - Size |
+/// | - Raws (element) | <- First DebugLineTableRowRef element
+/// +------------------+
+/// | element[1] | <- Additional DebugLineTableRowRef elements
+/// | element[2] | stored immediately after the object
+/// | ... |
+/// | element[Size-1] |
+/// +------------------+
+///
+/// PERFORMANCE BENEFITS:
+/// - Single memory allocation: All elements are stored in one contiguous block,
+/// eliminating the need for separate heap allocations for the array.
+/// - No extra dereferencing: Elements are accessed directly via pointer
+/// arithmetic (beginPtr() + offset) rather than through an additional
+/// pointer indirection.
+/// - Cache locality: All elements are guaranteed to be adjacent in memory,
+/// improving cache performance during iteration.
+/// - Memory efficiency: No overhead from separate pointer storage or
+/// fragmented allocations.
+///
+/// The 'Raws' member serves as both the first element storage and the base
+/// address for pointer arithmetic to access subsequent elements.
+class ClusteredRows {
+public:
+ ArrayRef<DebugLineTableRowRef> getRows() const {
+ return ArrayRef<DebugLineTableRowRef>(beginPtrConst(), Size);
+ }
+ uint64_t size() const { return Size; }
+ static const ClusteredRows *fromSMLoc(const SMLoc &Loc) {
+ return reinterpret_cast<const ClusteredRows *>(Loc.getPointer());
+ }
+ SMLoc toSMLoc() const {
+ return SMLoc::getFromPointer(reinterpret_cast<const char *>(this));
+ }
+
+ template <typename T> void populate(const T Vec) {
+ assert(Vec.size() == Size && "");
+ DebugLineTableRowRef *CurRawPtr = beginPtr();
+ for (DebugLineTableRowRef RowRef : Vec) {
+ *CurRawPtr = RowRef;
+ ++CurRawPtr;
+ }
+ }
+
+private:
+ uint64_t Size;
+ DebugLineTableRowRef Raws;
+
+ ClusteredRows(uint64_t Size) : Size(Size) {}
+ static uint64_t getTotalSize(uint64_t Size) {
+ assert(Size > 0 && "Size must be greater than 0");
+ return sizeof(ClusteredRows) + (Size - 1) * sizeof(DebugLineTableRowRef);
+ }
+ const DebugLineTableRowRef *beginPtrConst() const {
+ return reinterpret_cast<const DebugLineTableRowRef *>(&Raws);
+ }
+ DebugLineTableRowRef *beginPtr() {
+ return reinterpret_cast<DebugLineTableRowRef *>(&Raws);
+ }
+
+ friend class ClasteredRowsContainer;
+};
+
+/// ClasteredRowsContainer manages the lifecycle of ClusteredRows objects.
+class ClasteredRowsContainer {
+public:
+ ClusteredRows *createClusteredRows(uint64_t Size) {
+ auto *CR = new (std::malloc(ClusteredRows::getTotalSize(Size)))
+ ClusteredRows(Size);
+ Clusters.push_back(CR);
+ return CR;
+ }
+ ~ClasteredRowsContainer() {
+ for (auto *CR : Clusters)
+ std::free(CR);
+ }
+
+private:
+ std::vector<ClusteredRows *> Clusters;
+};
+
} // namespace bolt
} // namespace llvm
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 320e38682bdba..92c160db4bcb1 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1916,18 +1916,23 @@ bool BinaryContext::isMarker(const SymbolRef &Symbol) const {
static void printDebugInfo(raw_ostream &OS, const MCInst &Instruction,
const BinaryFunction *Function,
DWARFContext *DwCtx) {
- DebugLineTableRowRef RowRef =
- DebugLineTableRowRef::fromSMLoc(Instruction.getLoc());
- if (RowRef == DebugLineTableRowRef::NULL_ROW)
+ const ClusteredRows *LineTableRows =
+ ClusteredRows::fromSMLoc(Instruction.getLoc());
+ if (LineTableRows == nullptr)
return;
+ // File name and line number should be the same for all CUs.
+ // So it is sufficient to check the first one.
+ DebugLineTableRowRef RowRef = LineTableRows->getRows().front();
const DWARFDebugLine::LineTable *LineTable = DwCtx->getLineTableForUnit(
DwCtx->getCompileUnitForOffset(RowRef.DwCompileUnitIndex));
- assert(LineTable && "line table expected for instruction with debug info");
+ if (!LineTable)
+ return;
const DWARFDebugLine::Row &Row = LineTable->Rows[RowRef.RowIndex - 1];
StringRef FileName = "";
+
if (std::optional<const char *> FName =
dwarf::toString(LineTable->Prologue.getFileNameEntry(Row.File).Name))
FileName = *FName;
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index 34bda7403d259..8862f0680cb7e 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -437,9 +437,9 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function,
Streamer.emitELFSize(StartSymbol, SizeExpr);
}
- // TODO: Emit line info end for all the CUs that contain the function.
if (opts::UpdateDebugSections && !Function.getDWARFUnits().empty())
- emitLineInfoEnd(Function, EndSymbol, Function.getDWARFUnits().front());
+ for (DWARFUnit *Unit : Function.getDWARFUnits())
+ emitLineInfoEnd(Function, EndSymbol, Unit);
// Exception handling info for the function.
emitLSDA(Function, FF);
@@ -681,64 +681,92 @@ void BinaryEmitter::emitConstantIslands(BinaryFunction &BF, bool EmitColdPart,
SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
SMLoc PrevLoc, bool FirstInstr,
MCSymbol *&InstrLabel) {
- // TODO: implment emitting into line tables corresponding to multiple CUs
- DWARFUnit *FunctionCU = BF.getDWARFUnits().front();
- const DWARFDebugLine::LineTable *FunctionLineTable =
- BF.getDWARFLineTableForUnit(FunctionCU);
- assert(FunctionCU && "cannot emit line info for function without CU");
-
- DebugLineTableRowRef RowReference = DebugLineTableRowRef::fromSMLoc(NewLoc);
-
- // Check if no new line info needs to be emitted.
- if (RowReference == DebugLineTableRowRef::NULL_ROW ||
+ if (NewLoc.getPointer() == nullptr ||
NewLoc.getPointer() == PrevLoc.getPointer())
return PrevLoc;
+ const ClusteredRows *Cluster = ClusteredRows::fromSMLoc(NewLoc);
+
+ auto addToLineTable = [&](DebugLineTableRowRef RowReference,
+ const DWARFUnit *TargetCU, unsigned Flags,
+ MCSymbol *InstrLabel,
+ const DWARFDebugLine::Row &CurrentRow) {
+ const uint64_t TargetUnitIndex = TargetCU->getOffset();
+ unsigned TargetFilenum = CurrentRow.File;
+ const uint32_t CurrentUnitIndex = RowReference.DwCompileUnitIndex;
+ // If the CU id from the current instruction location does not
+ // match the target CU id, it means that we have come across some
+ // inlined code (by BOLT). We must look up the CU for the instruction's
+ // original function and get the line table from that.
+ if (TargetUnitIndex != CurrentUnitIndex) {
+ // Add filename from the inlined function to the current CU.
+ TargetFilenum = BC.addDebugFilenameToUnit(
+ TargetUnitIndex, CurrentUnitIndex, CurrentRow.File);
+ }
+ BC.Ctx->setCurrentDwarfLoc(TargetFilenum, CurrentRow.Line,
+ CurrentRow.Column, Flags, CurrentRow.Isa,
+ CurrentRow.Discriminator);
+ const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc();
+ BC.Ctx->clearDwarfLocSeen();
+ auto &MapLineEntries = BC.getDwarfLineTable(TargetUnitIndex)
+ .getMCLineSections()
+ .getMCLineEntries();
+ const auto *It = MapLineEntries.find(Streamer.getCurrentSectionOnly());
+ auto NewLineEntry = MCDwarfLineEntry(InstrLabel, DwarfLoc);
+
+ // Check if line table exists and has entries before doing comparison
+ if (It != MapLineEntries.end() && !It->second.empty()) {
+ // Check if the new line entry has the same debug info as the last one
+ // to avoid duplicates. We don't compare labels since different
+ // instructions can have the same line info.
+ const auto &LastEntry = It->second.back();
+ if (LastEntry.getFileNum() == NewLineEntry.getFileNum() &&
+ LastEntry.getLine() == NewLineEntry.getLine() &&
+ LastEntry.getColumn() == NewLineEntry.getColumn() &&
+ LastEntry.getFlags() == NewLineEntry.getFlags() &&
+ LastEntry.getIsa() == NewLineEntry.getIsa() &&
+ LastEntry.getDiscriminator() == NewLineEntry.getDiscriminator())
+ return;
+ }
- unsigned CurrentFilenum = 0;
- const DWARFDebugLine::LineTable *CurrentLineTable = FunctionLineTable;
-
- // If the CU id from the current instruction location does not
- // match the CU id from the current function, it means that we
- // have come across some inlined code. We must look up the CU
- // for the instruction's original function and get the line table
- // from that.
- const uint64_t FunctionUnitIndex = FunctionCU->getOffset();
- const uint32_t CurrentUnitIndex = RowReference.DwCompileUnitIndex;
- if (CurrentUnitIndex != FunctionUnitIndex) {
- CurrentLineTable = BC.DwCtx->getLineTableForUnit(
- BC.DwCtx->getCompileUnitForOffset(CurrentUnitIndex));
- // Add filename from the inlined function to the current CU.
- CurrentFilenum = BC.addDebugFilenameToUnit(
- FunctionUnitIndex, CurrentUnitIndex,
- CurrentLineTable->Rows[RowReference.RowIndex - 1].File);
- }
-
- const DWARFDebugLine::Row &CurrentRow =
- CurrentLineTable->Rows[RowReference.RowIndex - 1];
- if (!CurrentFilenum)
- CurrentFilenum = CurrentRow.File;
-
- unsigned Flags = (DWARF2_FLAG_IS_STMT * CurrentRow.IsStmt) |
- (DWARF2_FLAG_BASIC_BLOCK * CurrentRow.BasicBlock) |
- (DWARF2_FLAG_PROLOGUE_END * CurrentRow.PrologueEnd) |
- (DWARF2_FLAG_EPILOGUE_BEGIN * CurrentRow.EpilogueBegin);
-
- // Always emit is_stmt at the beginning of function fragment.
- if (FirstInstr)
- Flags |= DWARF2_FLAG_IS_STMT;
-
- BC.Ctx->setCurrentDwarfLoc(CurrentFilenum, CurrentRow.Line, CurrentRow.Column,
- Flags, CurrentRow.Isa, CurrentRow.Discriminator);
- const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc();
- BC.Ctx->clearDwarfLocSeen();
+ BC.getDwarfLineTable(TargetUnitIndex)
+ .getMCLineSections()
+ .addLineEntry(NewLineEntry, Streamer.getCurrentSectionOnly());
+ };
if (!InstrLabel)
InstrLabel = BC.Ctx->createTempSymbol();
-
- BC.getDwarfLineTable(FunctionUnitIndex)
- .getMCLineSections()
- .addLineEntry(MCDwarfLineEntry(InstrLabel, DwarfLoc),
- Streamer.getCurrentSectionOnly());
+ for (DebugLineTableRowRef RowReference : Cluster->getRows()) {
+ const DWARFDebugLine::LineTable *CurrentLineTable =
+ BC.DwCtx->getLineTableForUnit(
+ BC.DwCtx->getCompileUnitForOffset(RowReference.DwCompileUnitIndex));
+ const DWARFDebugLine::Row &CurrentRow =
+ CurrentLineTable->Rows[RowReference.RowIndex - 1];
+ unsigned Flags = (DWARF2_FLAG_IS_STMT * CurrentRow.IsStmt) |
+ (DWARF2_FLAG_BASIC_BLOCK * CurrentRow.BasicBlock) |
+ (DWARF2_FLAG_PROLOGUE_END * CurrentRow.PrologueEnd) |
+ (DWARF2_FLAG_EPILOGUE_BEGIN * CurrentRow.EpilogueBegin);
+
+ // Always emit is_stmt at the beginning of function fragment.
+ if (FirstInstr)
+ Flags |= DWARF2_FLAG_IS_STMT;
+ const auto &FunctionDwarfUnits = BF.getDWARFUnits();
+ const auto *It = std::find_if(
+ FunctionDwarfUnits.begin(), FunctionDwarfUnits.end(),
+ [RowReference](const DWARFUnit *Unit) {
+ return Unit->getOffset() == RowReference.DwCompileUnitIndex;
+ });
+ if (It != FunctionDwarfUnits.end()) {
+ addToLineTable(RowReference, *It, Flags, InstrLabel, CurrentRow);
+ continue;
+ }
+ // This rows is from CU that did not contain the original function.
+ // This might happen if BOLT moved/inlined that instruction from other CUs.
+ // In this case, we need to insert it to all CUs that the function
+ // originally beloned to.
+ for (const DWARFUnit *Unit : BF.getDWARFUnits()) {
+ addToLineTable(RowReference, Unit, Flags, InstrLabel, CurrentRow);
+ }
+ }
return NewLoc;
}
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 72302c4170e64..208c8d0fb4798 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -179,37 +179,29 @@ template <typename R> static bool emptyRange(const R &Range) {
}
/// Gets debug line information for the instruction located at the given
-/// address in the original binary. The SMLoc's pointer is used
-/// to point to this information, which is represented by a
-/// DebugLineTableRowRef. The returned pointer is null if no debug line
-/// information for this instruction was found.
-static SMLoc findDebugLineInformationForInstructionAt(
+/// address in the original binary. Returns an optional DebugLineTableRowRef
+/// that references the corresponding row in the DWARF line table. Since binary
+/// functions can span multiple compilation units, this function helps
+/// associate instructions with their debug line information from the
+/// appropriate CU. Returns std::nullopt if no debug line information for
+/// this instruction was found.
+static std::optional<DebugLineTableRowRef>
+findDebugLineInformationForInstructionAt(
uint64_t Address, DWARFUnit *Unit,
const DWARFDebugLine::LineTable *LineTable) {
- // We use the pointer in SMLoc to store an instance of DebugLineTableRowRef,
- // which occupies 64 bits. Thus, we can only proceed if the struct fits into
- // the pointer itself.
- static_assert(
- sizeof(decltype(SMLoc().getPointer())) >= sizeof(DebugLineTableRowRef),
- "Cannot fit instruction debug line information into SMLoc's pointer");
-
- SMLoc NullResult = DebugLineTableRowRef::NULL_ROW.toSMLoc();
uint32_t RowIndex = LineTable->lookupAddress(
{Address, object::SectionedAddress::UndefSection});
if (RowIndex == LineTable->UnknownRowIndex)
- return NullResult;
+ return std::nullopt;
assert(RowIndex < LineTable->Rows.size() &&
"Line Table lookup returned invalid index.");
- decltype(SMLoc().getPointer()) Ptr;
- DebugLineTableRowRef *InstructionLocation =
- reinterpret_cast<DebugLineTableRowRef *>(&Ptr);
-
- InstructionLocation->DwCompileUnitIndex = Unit->getOffset();
- InstructionLocation->RowIndex = RowIndex + 1;
+ DebugLineTableRowRef InstructionLocation;
+ InstructionLocation.DwCompileUnitIndex = Unit->getOffset();
+ InstructionLocation.RowIndex = RowIndex + 1;
- return SMLoc::getFromPointer(Ptr);
+ return InstructionLocation;
}
static std::string buildSectionName(StringRef Prefix, StringRef Name,
@@ -1496,15 +1488,23 @@ Error BinaryFunction::disassemble() {
}
add_instruction:
- // TODO: Handle multiple DWARF compilation units properly.
- // For now, use the first unit if available.
if (!getDWARFUnits().empty()) {
- DWARFUnit *FirstUnit = getDWARFUnits().front();
- const DWARFDebugLine::LineTable *LineTable =
- getDWARFLineTableForUnit(FirstUnit);
- if (LineTable) {
- Instruction.setLoc(findDebugLineInformationForInstructionAt(
- AbsoluteInstrAddr, FirstUnit, LineTable));
+ SmallVector<DebugLineTableRowRef, 1> Rows;
+ for (DWARFUnit *Unit : getDWARFUnits()) {
+ const DWARFDebugLine::LineTable *LineTable =
+ getDWARFLineTableForUnit(Unit);
+ if (!LineTable)
+ continue;
+ if (std::optional<DebugLineTableRowRef> RowRef =
+ findDebugLineInformationForInstructionAt(AbsoluteInstrAddr,
+ Unit, LineTable))
+ Rows.emplace_back(*RowRef);
+ }
+ if (!Rows.empty()) {
+ ClusteredRows *Cluster =
+ BC.ClasteredRows.createClusteredRows(Rows.size());
+ Cluster->populate(Rows);
+ Instruction.setLoc(Cluster->toSMLoc());
}
}
diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp
index 521eb8d91bbc0..e05f28f08572c 100644
--- a/bolt/lib/Core/DebugData.cpp
+++ b/bolt/lib/Core/DebugData.cpp
@@ -101,8 +101,6 @@ std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE,
return findAttributeInfo(DIE, AbbrevDecl, *Index);
}
-const DebugLineTableRowRef DebugLineTableRowRef::NULL_ROW{0, 0};
-
LLVM_ATTRIBUTE_UNUSED
static void printLE64(const std::string &S) {
for (uint32_t I = 0, Size = S.size(); I < Size; ++I) {
diff --git a/bolt/test/Inputs/multi-cu-common.h b/bolt/test/Inputs/multi-cu-common.h
new file mode 100644
index 0000000000000..aeb8076305dce
--- /dev/null
+++ b/bolt/test/Inputs/multi-cu-common.h
@@ -0,0 +1,10 @@
+#ifndef MULTI_CU_COMMON_H
+#define MULTI_CU_COMMON_H
+
+static inline int common_inline_function(int x) {
+ int result = x * 2;
+ result += 10;
+ return result;
+}
+
+#endif // MULTI_CU_COMMON_H
diff --git a/bolt/test/Inputs/multi-cu-file1.c b/bolt/test/Inputs/multi-cu-file1.c
new file mode 100644
index 0000000000000..f3528b2acddb8
--- /dev/null
+++ b/bolt/test/Inputs/multi-cu-file1.c
@@ -0,0 +1,9 @@
+#include "multi-cu-common.h"
+#include <stdio.h>
+
+int main() {
+ int value = 5;
+ int result = common_inline_function(value);
+ printf("File1: Result is %d\n", result);
+ return 0;
+}
diff --git a/bolt/test/Inputs/multi-cu-file2.c b/bolt/test/Inputs/multi-cu-file2.c
new file mode 100644
index 0000000000000..f33af72595afe
--- /dev/null
+++ b/bolt/test/Inputs/multi-cu-file2.c
@@ -0,0 +1,8 @@
+#include "multi-cu-common.h"
+#include <stdio.h>
+
+void helper_function() {
+ int value = 10;
+ int result = common_inline_function(value);
+ printf("File2: Helper result is %d\n", result);
+}
diff --git a/bolt/test/Inputs/process-debug-line.sh b/bolt/test/Inputs/process-debug-line.sh
new file mode 100755
index 0000000000000..b30408df922eb
--- /dev/null
+++ b/bolt/test/Inputs/process-debug-line.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+# Script to process llvm-dwarfdump --debug-line output and create a normalized table
+# Usage: process-debug-line.sh <debug-line.txt>
+#
+# Output format: CU_FILE LINE COLUMN FILE_NAME [additional_info]
+# This strips addresses to make rows unique and adds context about which CU and file each line belongs to
+
+if [ $# -ne 1 ]; then
+ echo "Usage: $0 <debug-line.txt>" >&2
+ exit 1
+fi
+
+debug_line_file="$1"
+
+if [ ! -f "$debug_line_file" ]; then
+ echo "Error: File '$debug_line_file' not found" >&2
+ exit 1
+fi
+
+awk '
+BEGIN {
+ cu_count = 0
+ current_cu_file = ""
+ # Initialize file names array
+ for (i = 0; i < 100; i++) current_file_names[i] = ""
+}
+
+# Track debug_line sections (new CU)
+/^debug_line\[/ {
+ cu_count++
+ current_cu_file = ""
+ # Clear file names array for new CU
+ for (i = 0; i < 100; i++) current_file_names[i] = ""
+ next
+}
+
+# Capture file names and their indices
+/^file_names\[.*\]:/ {
+ # Extract file index using more portable regex
+ if (match($0, /file_names\[[[:space:]]*([0-9]+)\]:/, arr)) {
+ file_index = arr[1]
+ } else {
+ # Fallback parsing
+ gsub(/file_names\[/, "", $0)
+ gsub(/\]:.*/, "", $0)
+ gsub(/[[:space:]]/, "", $0)
+ file_index = $0
+ }
+
+ getline # Read the next line which contains the actual filename
+ if (match($0, /name:[[:space:]]*"([^"]*)"/, name_arr)) {
+ filename = name_arr[1]
+ current_file_names[file_index] = filename
+
+ # Extract basename for main CU file (first .c/.cpp/.cc file we see)
+ if (current_cu_file == "" && match(filename, /([^\/]*\.(c|cpp|cc))$/, cu_arr)) {
+ current_cu_file = cu_arr[1]
+ }
+ }
+ next
+}
+
+# Process line table entries
+/^0x[0-9a-f]+/ {
+ # Parse the line entry: Address Line Column File ISA Discriminator OpIndex Flags
+ if (NF >= 4) {
+ line = $2
+ column = $3
+ file_index = $4
+
+ # Get the filename for this file index
+ filename = current_file_names[file_index]
+ if (filename == "") {
+ filename = "UNKNOWN_FILE_" file_index
+ } else {
+ # Extract just the basename using portable method
+ if (match(filename, /([^\/]*)$/, basename_arr)) {
+ filename = basename_arr[1]
+ } else {
+ # Fallback: use gsub
+ gsub(/.*\//, "", filename)
+ }
+ }
+
+ # Build additional info (flags, etc.)
+ additional_info = ""
+ for (i = 8; i <= NF; i++) {
+ if (additional_info != "") additional_info = additional_info " "
+ additional_info = additional_info $i
+ }
+
+ # Output normalized row: CU_FILE LINE COLUMN FILE_NAME [additional_info]
+ printf "%s %s %s %s", current_cu_file, line, column, filename
+ if (additional_info != "") {
+ printf " %s", additional_info
+ }
+ printf "\n"
+ }
+}
+' "$debug_line_file"
diff --git a/bolt/test/X86/multi-cu-debug-line.test b/bolt/test/X86/multi-cu-debug-line.test
new file mode 100644
index 0000000000000..430b281445db5
--- /dev/null
+++ b/bolt/test/X86/multi-cu-debug-line.test
@@ -0,0 +1,108 @@
+## Test that BOLT correctly handles debug line information for functions
+## that belong to multiple compilation units (e.g., inline functions in
+## common header files). The test covers two scenarios:
+## 1. Normal processing: .debug_line section shows lines for the function
+## in all CUs where it was compiled, with no duplicate rows within CUs
+## 2. Functions not processed: When BOLT doesn't process functions (using
+## --funcs with nonexistent function), original debug info is preserved
+
+# REQUIRES: system-linux
+
+## Compile test files with debug info
+# RUN: %clang %cflags -O0 -g %S/../Inputs/multi-cu-file1.c %S/../Inputs/multi-cu-file2.c \
+# RUN: -I%S/../Inputs -o %t.exe -Wl,-q
+
+## Test 1: Normal BOLT processing (functions are processed/optimized)
+# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
+# RUN: llvm-dwarfdump --debug-line %t.bolt > %t.debug-line.txt
+# RUN: FileCheck %s --check-prefix=BASIC --input-file %t.debug-line.txt
+
+## Check that debug line information is present for both compilation units
+# BASIC: debug_line[{{.*}}]
+# BASIC: file_names[{{.*}}]:
+# BASIC: name: "{{.*}}multi-cu-file1.c"
+# BASIC: debug_line[{{.*}}]
+# BASIC: file_names[{{.*}}]:
+# BASIC: name: "{{.*}}multi-cu-file2.c"
+
+## Use our helper script to create a normalized table without addresses
+# RUN: %S/../Inputs/process-debug-line.sh %t.debug-line.txt > %t.normalized-debug-line.txt
+# RUN: FileCheck %s --check-prefix=NORMALIZED --input-file %t.normalized-debug-line.txt
+
+## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
+## in both compilation units
+# NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h
+
+## Verify that we have line entries for the inline function in multiple CUs
+## by checking that the header file appears multiple times in different contexts
+# RUN: grep -c "multi-cu-common.h" %t.debug-line.txt > %t.header-count.txt
+# RUN: FileCheck %s --check-prefix=MULTI-CU --input-file %t.header-count.txt
+
+## The header should appear in debug line info for multiple CUs
+# MULTI-CU: {{[2-9]|[1-9][0-9]+}}
+
+## Check that there are no duplicate line table rows within the same CU
+## This verifies the fix for the bug where duplicate entries were created
+# RUN: sort %t.normalized-debug-line.txt | uniq -c | \
+# RUN: awk '$1 > 1 {print "DUPLICATE_ROW: " $0}' > %t.duplicates.txt
+# RUN: FileCheck %s --check-prefix=NO-DUPLICATES --input-file %t.duplicates.txt --allow-empty
+
+## Should have no duplicate normalized rows (file should be empty)
+## Note: Cross-CU duplicates are expected and valid (same function in different CUs)
+## but within-CU duplicates would indicate a bug
+# NO-DUPLICATES-NOT: DUPLICATE_ROW
+
+## Test 2: Functions not processed by BOLT (using --funcs with nonexistent function)
+## This tests the code path where BOLT preserves original debug info
+# RUN: llvm-bolt %t.exe -o %t.not-emitted.bolt --update-debug-sections --funcs=nonexistent_function
+# RUN: llvm-dwarfdump --debug-line %t.not-emitted.bolt > %t.not-emitted.debug-line.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-BASIC --input-file %t.not-emitted.debug-line.txt
+
+## Check that debug line information is still present for both compilation units when functions aren't processed
+# PRESERVED-BASIC: debug_line[{{.*}}]
+# PRESERVED-BASIC: file_names[{{.*}}]:
+# PRESERVED-BASIC: name: "{{.*}}multi-cu-file1.c"
+# PRESERVED-BASIC: debug_line[{{.*}}]
+# PRESERVED-BASIC: file_names[{{.*}}]:
+# PRESERVED-BASIC: name: "{{.*}}multi-cu-file2.c"
+
+## Create normalized output for the not-emitted case
+# RUN: %S/../Inputs/process-debug-line.sh %t.not-emitted.debug-line.txt > %t.not-emitted.normalized.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-NORMALIZED --input-file %t.not-emitted.normalized.txt
+
+## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
+## in both compilation units (preserved from original)
+# PRESERVED-NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h
+
+## Verify that we have line entries for the inline function in multiple CUs (preserved)
+## by checking that the header file appears multiple times in different contexts
+# RUN: grep -c "multi-cu-common.h" %t.not-emitted.debug-line.txt > %t.preserved-header-count.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-MULTI-CU --input-file %t.preserved-header-count.txt
+
+## The header should appear in debug line info for multiple CUs (preserved from original)
+# PRESERVED-MULTI-CU: {{[2-9]|[1-9][0-9]+}}
+
+## Check that original debug info is preserved for main functions
+# RUN: grep "multi-cu-file1.c.*multi-cu-file1.c" %t.not-emitted.normalized.txt > %t.preserved-main.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-MAIN --input-file %t.preserved-main.txt
+
+# PRESERVED-MAIN: multi-cu-file1.c {{[0-9]+}} {{[0-9]+}} multi-cu-file1.c
+
+## Check that original debug info is preserved for file2 functions
+# RUN: grep "multi-cu-file2.c.*multi-cu-file2.c" %t.not-emitted.normalized.txt > %t.preserved-file2.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-FILE2 --input-file %t.preserved-file2.txt
+
+# PRESERVED-FILE2: multi-cu-file2.c {{[0-9]+}} {{[0-9]+}} multi-cu-file2.c
+
+## Note: We do not check for duplicates in Test 2 since we are preserving original debug info as-is
+## and the original may contain patterns that would be flagged as duplicates by our normalization
\ No newline at end of file
diff --git a/bolt/test/perf2bolt/Inputs/perf_test.lds b/bolt/test/perf2bolt/Inputs/perf_test.lds
index 66d925a05bebc..c2704d73a638c 100644
--- a/bolt/test/perf2bolt/Inputs/perf_test.lds
+++ b/bolt/test/perf2bolt/Inputs/perf_test.lds
@@ -1,13 +1,12 @@
SECTIONS {
- . = SIZEOF_HEADERS;
+ . = 0x400000 + SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
- . = 0x212e8;
.dynsym : { *(.dynsym) }
- . = 0x31860;
+ . = 0x801000;
.text : { *(.text*) }
- . = 0x41c20;
+ . = 0x803000;
.fini_array : { *(.fini_array) }
- . = 0x54e18;
+ . = 0x805000;
.data : { *(.data) }
-}
\ No newline at end of file
+}
diff --git a/bolt/unittests/Core/CMakeLists.txt b/bolt/unittests/Core/CMakeLists.txt
index f10b0d9472067..297dec7449202 100644
--- a/bolt/unittests/Core/CMakeLists.txt
+++ b/bolt/unittests/Core/CMakeLists.txt
@@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
add_bolt_unittest(CoreTests
BinaryContext.cpp
+ ClusteredRows.cpp
MCPlusBuilder.cpp
MemoryMaps.cpp
DynoStats.cpp
diff --git a/bolt/unittests/Core/ClusteredRows.cpp b/bolt/unittests/Core/ClusteredRows.cpp
new file mode 100644
index 0000000000000..5901f9ac5aaaa
--- /dev/null
+++ b/bolt/unittests/Core/ClusteredRows.cpp
@@ -0,0 +1,152 @@
+//===- bolt/unittest/Core/ClusteredRows.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Core/DebugData.h"
+#include "llvm/Support/SMLoc.h"
+#include "gtest/gtest.h"
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::bolt;
+
+namespace {
+
+class ClusteredRowsTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ Container = std::make_unique<ClasteredRowsContainer>();
+ }
+
+ std::unique_ptr<ClasteredRowsContainer> Container;
+};
+
+TEST_F(ClusteredRowsTest, CreateSingleElement) {
+ ClusteredRows *CR = Container->createClusteredRows(1);
+ ASSERT_NE(CR, nullptr);
+ EXPECT_EQ(CR->size(), 1u);
+
+ // Test population with single element
+ std::vector<DebugLineTableRowRef> TestRefs = {
+ {42, 100}
+ };
+ CR->populate(TestRefs);
+
+ ArrayRef<DebugLineTableRowRef> Rows = CR->getRows();
+ EXPECT_EQ(Rows.size(), 1u);
+ EXPECT_EQ(Rows[0].DwCompileUnitIndex, 42u);
+ EXPECT_EQ(Rows[0].RowIndex, 100u);
+}
+
+TEST_F(ClusteredRowsTest, CreateMultipleElements) {
+ ClusteredRows *CR = Container->createClusteredRows(3);
+ ASSERT_NE(CR, nullptr);
+ EXPECT_EQ(CR->size(), 3u);
+
+ // Test population with multiple elements
+ std::vector<DebugLineTableRowRef> TestRefs = {
+ {10, 20},
+ {30, 40},
+ {50, 60}
+ };
+ CR->populate(TestRefs);
+
+ ArrayRef<DebugLineTableRowRef> Rows = CR->getRows();
+ EXPECT_EQ(Rows.size(), 3u);
+
+ EXPECT_EQ(Rows[0].DwCompileUnitIndex, 10u);
+ EXPECT_EQ(Rows[0].RowIndex, 20u);
+
+ EXPECT_EQ(Rows[1].DwCompileUnitIndex, 30u);
+ EXPECT_EQ(Rows[1].RowIndex, 40u);
+
+ EXPECT_EQ(Rows[2].DwCompileUnitIndex, 50u);
+ EXPECT_EQ(Rows[2].RowIndex, 60u);
+}
+
+TEST_F(ClusteredRowsTest, SMLoc_Conversion) {
+ ClusteredRows *CR = Container->createClusteredRows(2);
+ ASSERT_NE(CR, nullptr);
+
+ // Test SMLoc conversion
+ SMLoc Loc = CR->toSMLoc();
+ EXPECT_TRUE(Loc.isValid());
+
+ // Test round-trip conversion
+ const ClusteredRows *CR2 = ClusteredRows::fromSMLoc(Loc);
+ EXPECT_EQ(CR, CR2);
+ EXPECT_EQ(CR2->size(), 2u);
+}
+
+TEST_F(ClusteredRowsTest, PopulateWithArrayRef) {
+ ClusteredRows *CR = Container->createClusteredRows(4);
+ ASSERT_NE(CR, nullptr);
+
+ // Test population with ArrayRef
+ DebugLineTableRowRef TestArray[] = {
+ {1, 2},
+ {3, 4},
+ {5, 6},
+ {7, 8}
+ };
+ ArrayRef<DebugLineTableRowRef> TestRefs(TestArray, 4);
+ CR->populate(TestRefs);
+
+ ArrayRef<DebugLineTableRowRef> Rows = CR->getRows();
+ EXPECT_EQ(Rows.size(), 4u);
+
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(Rows[i].DwCompileUnitIndex, TestArray[i].DwCompileUnitIndex);
+ EXPECT_EQ(Rows[i].RowIndex, TestArray[i].RowIndex);
+ }
+}
+
+TEST_F(ClusteredRowsTest, MultipleClusteredRows) {
+ // Test creating multiple ClusteredRows objects
+ ClusteredRows *CR1 = Container->createClusteredRows(2);
+ ClusteredRows *CR2 = Container->createClusteredRows(3);
+ ClusteredRows *CR3 = Container->createClusteredRows(1);
+
+ ASSERT_NE(CR1, nullptr);
+ ASSERT_NE(CR2, nullptr);
+ ASSERT_NE(CR3, nullptr);
+
+ // Ensure they are different objects
+ EXPECT_NE(CR1, CR2);
+ EXPECT_NE(CR2, CR3);
+ EXPECT_NE(CR1, CR3);
+
+ // Verify sizes
+ EXPECT_EQ(CR1->size(), 2u);
+ EXPECT_EQ(CR2->size(), 3u);
+ EXPECT_EQ(CR3->size(), 1u);
+
+ // Populate each with different data
+ std::vector<DebugLineTableRowRef> TestRefs1 = {{100, 200}, {300, 400}};
+ std::vector<DebugLineTableRowRef> TestRefs2 = {{10, 20}, {30, 40}, {50, 60}};
+ std::vector<DebugLineTableRowRef> TestRefs3 = {{999, 888}};
+
+ CR1->populate(TestRefs1);
+ CR2->populate(TestRefs2);
+ CR3->populate(TestRefs3);
+
+ // Verify data integrity
+ ArrayRef<DebugLineTableRowRef> Rows1 = CR1->getRows();
+ ArrayRef<DebugLineTableRowRef> Rows2 = CR2->getRows();
+ ArrayRef<DebugLineTableRowRef> Rows3 = CR3->getRows();
+
+ EXPECT_EQ(Rows1[0].DwCompileUnitIndex, 100u);
+ EXPECT_EQ(Rows1[1].RowIndex, 400u);
+
+ EXPECT_EQ(Rows2[1].DwCompileUnitIndex, 30u);
+ EXPECT_EQ(Rows2[2].RowIndex, 60u);
+
+ EXPECT_EQ(Rows3[0].DwCompileUnitIndex, 999u);
+ EXPECT_EQ(Rows3[0].RowIndex, 888u);
+}
+
+} // namespace
>From b1d229a43a5c37b924929750365f64b1dee0726b Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Tue, 29 Jul 2025 14:04:57 -0700
Subject: [PATCH 03/12] Fix lint issues
---
bolt/unittests/Core/ClusteredRows.cpp | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/bolt/unittests/Core/ClusteredRows.cpp b/bolt/unittests/Core/ClusteredRows.cpp
index 5901f9ac5aaaa..a75209a75dfad 100644
--- a/bolt/unittests/Core/ClusteredRows.cpp
+++ b/bolt/unittests/Core/ClusteredRows.cpp
@@ -31,9 +31,7 @@ TEST_F(ClusteredRowsTest, CreateSingleElement) {
EXPECT_EQ(CR->size(), 1u);
// Test population with single element
- std::vector<DebugLineTableRowRef> TestRefs = {
- {42, 100}
- };
+ std::vector<DebugLineTableRowRef> TestRefs = {{42, 100}};
CR->populate(TestRefs);
ArrayRef<DebugLineTableRowRef> Rows = CR->getRows();
@@ -48,11 +46,7 @@ TEST_F(ClusteredRowsTest, CreateMultipleElements) {
EXPECT_EQ(CR->size(), 3u);
// Test population with multiple elements
- std::vector<DebugLineTableRowRef> TestRefs = {
- {10, 20},
- {30, 40},
- {50, 60}
- };
+ std::vector<DebugLineTableRowRef> TestRefs = {{10, 20}, {30, 40}, {50, 60}};
CR->populate(TestRefs);
ArrayRef<DebugLineTableRowRef> Rows = CR->getRows();
@@ -87,12 +81,7 @@ TEST_F(ClusteredRowsTest, PopulateWithArrayRef) {
ASSERT_NE(CR, nullptr);
// Test population with ArrayRef
- DebugLineTableRowRef TestArray[] = {
- {1, 2},
- {3, 4},
- {5, 6},
- {7, 8}
- };
+ DebugLineTableRowRef TestArray[] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
ArrayRef<DebugLineTableRowRef> TestRefs(TestArray, 4);
CR->populate(TestRefs);
>From 15ef19f9c9ab31d878457e3799c9278753d44c52 Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Tue, 29 Jul 2025 16:28:32 -0700
Subject: [PATCH 04/12] Modify awk script to make it work with older versions
---
bolt/test/Inputs/process-debug-line.sh | 52 ++++++++++++++------------
1 file changed, 28 insertions(+), 24 deletions(-)
diff --git a/bolt/test/Inputs/process-debug-line.sh b/bolt/test/Inputs/process-debug-line.sh
index b30408df922eb..44cbcd1e5984a 100755
--- a/bolt/test/Inputs/process-debug-line.sh
+++ b/bolt/test/Inputs/process-debug-line.sh
@@ -23,7 +23,9 @@ BEGIN {
cu_count = 0
current_cu_file = ""
# Initialize file names array
- for (i = 0; i < 100; i++) current_file_names[i] = ""
+ for (i = 0; i < 100; i++) {
+ current_file_names[i] = ""
+ }
}
# Track debug_line sections (new CU)
@@ -31,31 +33,34 @@ BEGIN {
cu_count++
current_cu_file = ""
# Clear file names array for new CU
- for (i = 0; i < 100; i++) current_file_names[i] = ""
+ for (i = 0; i < 100; i++) {
+ current_file_names[i] = ""
+ }
next
}
# Capture file names and their indices
/^file_names\[.*\]:/ {
- # Extract file index using more portable regex
- if (match($0, /file_names\[[[:space:]]*([0-9]+)\]:/, arr)) {
- file_index = arr[1]
- } else {
- # Fallback parsing
- gsub(/file_names\[/, "", $0)
- gsub(/\]:.*/, "", $0)
- gsub(/[[:space:]]/, "", $0)
- file_index = $0
- }
+ # Extract file index using simple string operations
+ line_copy = $0
+ gsub(/file_names\[/, "", line_copy)
+ gsub(/\]:.*/, "", line_copy)
+ gsub(/[ \t]/, "", line_copy)
+ file_index = line_copy
getline # Read the next line which contains the actual filename
- if (match($0, /name:[[:space:]]*"([^"]*)"/, name_arr)) {
- filename = name_arr[1]
+ # Extract filename from name: "filename" format
+ if (match($0, /name:[ \t]*"/)) {
+ filename = $0
+ gsub(/.*name:[ \t]*"/, "", filename)
+ gsub(/".*/, "", filename)
current_file_names[file_index] = filename
# Extract basename for main CU file (first .c/.cpp/.cc file we see)
- if (current_cu_file == "" && match(filename, /([^\/]*\.(c|cpp|cc))$/, cu_arr)) {
- current_cu_file = cu_arr[1]
+ if (current_cu_file == "" && match(filename, /\.(c|cpp|cc)$/)) {
+ cu_filename = filename
+ gsub(/.*\//, "", cu_filename)
+ current_cu_file = cu_filename
}
}
next
@@ -74,19 +79,18 @@ BEGIN {
if (filename == "") {
filename = "UNKNOWN_FILE_" file_index
} else {
- # Extract just the basename using portable method
- if (match(filename, /([^\/]*)$/, basename_arr)) {
- filename = basename_arr[1]
- } else {
- # Fallback: use gsub
- gsub(/.*\//, "", filename)
- }
+ # Extract just the basename
+ basename = filename
+ gsub(/.*\//, "", basename)
+ filename = basename
}
# Build additional info (flags, etc.)
additional_info = ""
for (i = 8; i <= NF; i++) {
- if (additional_info != "") additional_info = additional_info " "
+ if (additional_info != "") {
+ additional_info = additional_info " "
+ }
additional_info = additional_info $i
}
>From b2297d0a11b7ebce7443c4077bcbb99bf067f31d Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Tue, 29 Jul 2025 17:22:00 -0700
Subject: [PATCH 05/12] Minor fixes
---
bolt/include/bolt/Core/BinaryFunction.h | 2 +-
bolt/lib/Core/BinaryContext.cpp | 8 +++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 11d7d0f9765c4..81ea3951e253e 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -2420,7 +2420,7 @@ class BinaryFunction {
}
/// Return DWARF compile units for this function.
- const SmallVector<DWARFUnit *, 1> getDWARFUnits() const {
+ const SmallVector<DWARFUnit *, 1>& getDWARFUnits() const {
return DwarfUnitVec;
}
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 92c160db4bcb1..c3c085c36d909 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1718,9 +1718,15 @@ void BinaryContext::preprocessDebugInfo() {
// Clear debug info for functions from units that we are not going to process.
for (auto &KV : BinaryFunctions) {
BinaryFunction &BF = KV.second;
+ // Collect units to remove to avoid iterator invalidation
+ SmallVector<DWARFUnit *, 1> UnitsToRemove;
for (auto *Unit : BF.getDWARFUnits()) {
if (!ProcessedCUs.count(Unit))
- BF.removeDWARFUnit(Unit);
+ UnitsToRemove.push_back(Unit);
+ }
+ // Remove the collected units
+ for (auto *Unit : UnitsToRemove) {
+ BF.removeDWARFUnit(Unit);
}
}
>From 76fabbd9baae8f9128add51f299eacf24f36488d Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Wed, 30 Jul 2025 19:41:02 -0700
Subject: [PATCH 06/12] Fixed typos
---
bolt/include/bolt/Core/BinaryContext.h | 2 +-
bolt/include/bolt/Core/DebugData.h | 32 ++++++++------------------
bolt/lib/Core/BinaryFunction.cpp | 2 +-
bolt/unittests/Core/ClusteredRows.cpp | 4 ++--
4 files changed, 13 insertions(+), 27 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 48bc9a5d1f92c..72c8817daa714 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -292,7 +292,7 @@ class BinaryContext {
/// tables associated with instructions. Since binary functions can span
/// multiple compilation units, instructions may reference debug line
/// information from multiple CUs.
- ClasteredRowsContainer ClasteredRows;
+ ClusteredRowsContainer ClusteredRows;
// Setup MCPlus target builder
void initializeTarget(std::unique_ptr<MCPlusBuilder> TargetBuilder) {
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index 048594946d8a9..adbce0bb1d5b6 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -834,9 +834,6 @@ class DwarfLineTable {
};
/// ClusteredRows represents a collection of debug line table row references.
-/// Since a Binary function can belong to multiple compilation units (CUs),
-/// a single MCInst can have multiple debug line table rows associated with it
-/// from different CUs. This class manages such clustered row references.
///
/// MEMORY LAYOUT AND DESIGN:
/// This class uses a flexible array member pattern to store all
@@ -846,7 +843,7 @@ class DwarfLineTable {
/// +------------------+
/// | ClusteredRows | <- Object header (Size + first element)
/// | - Size |
-/// | - Raws (element) | <- First DebugLineTableRowRef element
+/// | - Rows (element) | <- First DebugLineTableRowRef element
/// +------------------+
/// | element[1] | <- Additional DebugLineTableRowRef elements
/// | element[2] | stored immediately after the object
@@ -854,18 +851,7 @@ class DwarfLineTable {
/// | element[Size-1] |
/// +------------------+
///
-/// PERFORMANCE BENEFITS:
-/// - Single memory allocation: All elements are stored in one contiguous block,
-/// eliminating the need for separate heap allocations for the array.
-/// - No extra dereferencing: Elements are accessed directly via pointer
-/// arithmetic (beginPtr() + offset) rather than through an additional
-/// pointer indirection.
-/// - Cache locality: All elements are guaranteed to be adjacent in memory,
-/// improving cache performance during iteration.
-/// - Memory efficiency: No overhead from separate pointer storage or
-/// fragmented allocations.
-///
-/// The 'Raws' member serves as both the first element storage and the base
+/// The 'Rows' member serves as both the first element storage and the base
/// address for pointer arithmetic to access subsequent elements.
class ClusteredRows {
public:
@@ -891,7 +877,7 @@ class ClusteredRows {
private:
uint64_t Size;
- DebugLineTableRowRef Raws;
+ DebugLineTableRowRef Rows;
ClusteredRows(uint64_t Size) : Size(Size) {}
static uint64_t getTotalSize(uint64_t Size) {
@@ -899,17 +885,17 @@ class ClusteredRows {
return sizeof(ClusteredRows) + (Size - 1) * sizeof(DebugLineTableRowRef);
}
const DebugLineTableRowRef *beginPtrConst() const {
- return reinterpret_cast<const DebugLineTableRowRef *>(&Raws);
+ return reinterpret_cast<const DebugLineTableRowRef *>(&Rows);
}
DebugLineTableRowRef *beginPtr() {
- return reinterpret_cast<DebugLineTableRowRef *>(&Raws);
+ return reinterpret_cast<DebugLineTableRowRef *>(&Rows);
}
- friend class ClasteredRowsContainer;
+ friend class ClusteredRowsContainer;
};
-/// ClasteredRowsContainer manages the lifecycle of ClusteredRows objects.
-class ClasteredRowsContainer {
+/// ClusteredRowsContainer manages the lifecycle of ClusteredRows objects.
+class ClusteredRowsContainer {
public:
ClusteredRows *createClusteredRows(uint64_t Size) {
auto *CR = new (std::malloc(ClusteredRows::getTotalSize(Size)))
@@ -917,7 +903,7 @@ class ClasteredRowsContainer {
Clusters.push_back(CR);
return CR;
}
- ~ClasteredRowsContainer() {
+ ~ClusteredRowsContainer() {
for (auto *CR : Clusters)
std::free(CR);
}
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 208c8d0fb4798..b79f17919c4dc 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1502,7 +1502,7 @@ Error BinaryFunction::disassemble() {
}
if (!Rows.empty()) {
ClusteredRows *Cluster =
- BC.ClasteredRows.createClusteredRows(Rows.size());
+ BC.ClusteredRows.createClusteredRows(Rows.size());
Cluster->populate(Rows);
Instruction.setLoc(Cluster->toSMLoc());
}
diff --git a/bolt/unittests/Core/ClusteredRows.cpp b/bolt/unittests/Core/ClusteredRows.cpp
index a75209a75dfad..4665022c91fdd 100644
--- a/bolt/unittests/Core/ClusteredRows.cpp
+++ b/bolt/unittests/Core/ClusteredRows.cpp
@@ -19,10 +19,10 @@ namespace {
class ClusteredRowsTest : public ::testing::Test {
protected:
void SetUp() override {
- Container = std::make_unique<ClasteredRowsContainer>();
+ Container = std::make_unique<ClusteredRowsContainer>();
}
- std::unique_ptr<ClasteredRowsContainer> Container;
+ std::unique_ptr<ClusteredRowsContainer> Container;
};
TEST_F(ClusteredRowsTest, CreateSingleElement) {
>From 13e82ab8ed916ca8ba2fd0ffcdbaf4940695f171 Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Thu, 31 Jul 2025 10:45:31 -0700
Subject: [PATCH 07/12] Made the test architecture independent
---
bolt/test/lit.cfg.py | 4 ++--
bolt/test/{X86 => }/multi-cu-debug-line.test | 14 +++++++-------
.../process-debug-line.sh => process-debug-line} | 0
3 files changed, 9 insertions(+), 9 deletions(-)
rename bolt/test/{X86 => }/multi-cu-debug-line.test (93%)
rename bolt/test/{Inputs/process-debug-line.sh => process-debug-line} (100%)
diff --git a/bolt/test/lit.cfg.py b/bolt/test/lit.cfg.py
index bef570ba50a04..0f2d3b1aa0eaf 100644
--- a/bolt/test/lit.cfg.py
+++ b/bolt/test/lit.cfg.py
@@ -10,8 +10,7 @@
import lit.util
from lit.llvm import llvm_config
-from lit.llvm.subst import ToolSubst
-from lit.llvm.subst import FindTool
+from lit.llvm.subst import FindTool, ToolSubst
# Configuration file for the 'lit' test runner.
@@ -138,6 +137,7 @@
unresolved="fatal",
extra_args=[link_fdata_cmd],
),
+ ToolSubst("process-debug-line", unresolved="fatal"),
ToolSubst("merge-fdata", unresolved="fatal"),
ToolSubst("llvm-readobj", unresolved="fatal"),
ToolSubst("llvm-dwp", unresolved="fatal"),
diff --git a/bolt/test/X86/multi-cu-debug-line.test b/bolt/test/multi-cu-debug-line.test
similarity index 93%
rename from bolt/test/X86/multi-cu-debug-line.test
rename to bolt/test/multi-cu-debug-line.test
index 430b281445db5..a94c901bbcc5a 100644
--- a/bolt/test/X86/multi-cu-debug-line.test
+++ b/bolt/test/multi-cu-debug-line.test
@@ -1,16 +1,16 @@
## Test that BOLT correctly handles debug line information for functions
## that belong to multiple compilation units (e.g., inline functions in
## common header files). The test covers two scenarios:
-## 1. Normal processing: .debug_line section shows lines for the function
+## 1. Normal processing: .debug_line section shows lines for the function
## in all CUs where it was compiled, with no duplicate rows within CUs
-## 2. Functions not processed: When BOLT doesn't process functions (using
+## 2. Functions not processed: When BOLT doesn't process functions (using
## --funcs with nonexistent function), original debug info is preserved
# REQUIRES: system-linux
## Compile test files with debug info
-# RUN: %clang %cflags -O0 -g %S/../Inputs/multi-cu-file1.c %S/../Inputs/multi-cu-file2.c \
-# RUN: -I%S/../Inputs -o %t.exe -Wl,-q
+# RUN: %clang %cflags -O0 -g %S/Inputs/multi-cu-file1.c %S/Inputs/multi-cu-file2.c \
+# RUN: -I%S/Inputs -o %t.exe -Wl,-q
## Test 1: Normal BOLT processing (functions are processed/optimized)
# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
@@ -26,7 +26,7 @@
# BASIC: name: "{{.*}}multi-cu-file2.c"
## Use our helper script to create a normalized table without addresses
-# RUN: %S/../Inputs/process-debug-line.sh %t.debug-line.txt > %t.normalized-debug-line.txt
+# RUN: process-debug-line %t.debug-line.txt > %t.normalized-debug-line.txt
# RUN: FileCheck %s --check-prefix=NORMALIZED --input-file %t.normalized-debug-line.txt
## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
@@ -72,7 +72,7 @@
# PRESERVED-BASIC: name: "{{.*}}multi-cu-file2.c"
## Create normalized output for the not-emitted case
-# RUN: %S/../Inputs/process-debug-line.sh %t.not-emitted.debug-line.txt > %t.not-emitted.normalized.txt
+# RUN: process-debug-line %t.not-emitted.debug-line.txt > %t.not-emitted.normalized.txt
# RUN: FileCheck %s --check-prefix=PRESERVED-NORMALIZED --input-file %t.not-emitted.normalized.txt
## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
@@ -105,4 +105,4 @@
# PRESERVED-FILE2: multi-cu-file2.c {{[0-9]+}} {{[0-9]+}} multi-cu-file2.c
## Note: We do not check for duplicates in Test 2 since we are preserving original debug info as-is
-## and the original may contain patterns that would be flagged as duplicates by our normalization
\ No newline at end of file
+## and the original may contain patterns that would be flagged as duplicates by our normalization
diff --git a/bolt/test/Inputs/process-debug-line.sh b/bolt/test/process-debug-line
similarity index 100%
rename from bolt/test/Inputs/process-debug-line.sh
rename to bolt/test/process-debug-line
>From 63ee4a331e4972f00b8fc360d51ec57b835621c3 Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Wed, 6 Aug 2025 12:29:08 -0700
Subject: [PATCH 08/12] Addressed comments: DenseMap, pointer to reference,
simplify if else
---
bolt/include/bolt/Core/BinaryFunction.h | 17 ++++++++---------
bolt/include/bolt/Core/DebugData.h | 2 +-
bolt/lib/Core/BinaryContext.cpp | 10 ++++------
bolt/lib/Core/BinaryEmitter.cpp | 24 ++++++++++--------------
bolt/lib/Core/BinaryFunction.cpp | 2 +-
5 files changed, 24 insertions(+), 31 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 81ea3951e253e..51b139a15e1a0 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -35,6 +35,7 @@
#include "bolt/Core/JumpTable.h"
#include "bolt/Core/MCPlus.h"
#include "bolt/Utils/NameResolver.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
@@ -424,7 +425,8 @@ class BinaryFunction {
unsigned LSDATypeEncoding{dwarf::DW_EH_PE_omit};
/// All compilation units this function belongs to.
- SmallVector<DWARFUnit *, 1> DwarfUnitVec;
+ /// Maps DWARF unit offset to the unit pointer.
+ DenseMap<uint64_t, DWARFUnit *> DwarfUnitMap;
/// Last computed hash value. Note that the value could be recomputed using
/// different parameters by every pass.
@@ -2409,19 +2411,16 @@ class BinaryFunction {
void
computeBlockHashes(HashFunction HashFunction = HashFunction::Default) const;
- void addDWARFUnit(DWARFUnit *Unit) { DwarfUnitVec.push_back(Unit); }
+ void addDWARFUnit(DWARFUnit *Unit) { DwarfUnitMap[Unit->getOffset()] = Unit; }
void removeDWARFUnit(DWARFUnit *Unit) {
- auto *It = std::find(DwarfUnitVec.begin(), DwarfUnitVec.end(), Unit);
- // If found, erase it
- if (It != DwarfUnitVec.end()) {
- DwarfUnitVec.erase(It);
- }
+ DwarfUnitMap.erase(Unit->getOffset());
}
/// Return DWARF compile units for this function.
- const SmallVector<DWARFUnit *, 1>& getDWARFUnits() const {
- return DwarfUnitVec;
+ /// Returns a reference to the map of DWARF unit offsets to units.
+ const DenseMap<uint64_t, DWARFUnit *> &getDWARFUnits() const {
+ return DwarfUnitMap;
}
const DWARFDebugLine::LineTable *
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index adbce0bb1d5b6..4c4f4d478b5e5 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -867,7 +867,7 @@ class ClusteredRows {
}
template <typename T> void populate(const T Vec) {
- assert(Vec.size() == Size && "");
+ assert(Vec.size() == Size && "Sizes must match");
DebugLineTableRowRef *CurRawPtr = beginPtr();
for (DebugLineTableRowRef RowRef : Vec) {
*CurRawPtr = RowRef;
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index c3c085c36d909..47ed62a53e3a8 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1700,11 +1700,9 @@ void BinaryContext::preprocessDebugInfo() {
// Go forward and add all units from ranges that cover the function
while (++It != AllRanges.end()) {
- if (It->LowPC <= FunctionAddress && FunctionAddress < It->HighPC) {
- Function.addDWARFUnit(It->Unit);
- } else {
+ if (It->LowPC > FunctionAddress || FunctionAddress >= It->HighPC)
break;
- }
+ Function.addDWARFUnit(It->Unit);
}
}
@@ -1712,7 +1710,7 @@ void BinaryContext::preprocessDebugInfo() {
for (const auto &KV : BinaryFunctions) {
const BinaryFunction &BF = KV.second;
if (shouldEmit(BF) && !BF.getDWARFUnits().empty())
- for (const DWARFUnit *Unit : BF.getDWARFUnits())
+ for (const auto &[_, Unit] : BF.getDWARFUnits())
ProcessedCUs.insert(Unit);
}
// Clear debug info for functions from units that we are not going to process.
@@ -1720,7 +1718,7 @@ void BinaryContext::preprocessDebugInfo() {
BinaryFunction &BF = KV.second;
// Collect units to remove to avoid iterator invalidation
SmallVector<DWARFUnit *, 1> UnitsToRemove;
- for (auto *Unit : BF.getDWARFUnits()) {
+ for (const auto &[_, Unit] : BF.getDWARFUnits()) {
if (!ProcessedCUs.count(Unit))
UnitsToRemove.push_back(Unit);
}
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index 8862f0680cb7e..8e5683ffa9831 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -438,7 +438,7 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function,
}
if (opts::UpdateDebugSections && !Function.getDWARFUnits().empty())
- for (DWARFUnit *Unit : Function.getDWARFUnits())
+ for (const auto &[_, Unit] : Function.getDWARFUnits())
emitLineInfoEnd(Function, EndSymbol, Unit);
// Exception handling info for the function.
@@ -687,10 +687,10 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
const ClusteredRows *Cluster = ClusteredRows::fromSMLoc(NewLoc);
auto addToLineTable = [&](DebugLineTableRowRef RowReference,
- const DWARFUnit *TargetCU, unsigned Flags,
- MCSymbol *InstrLabel,
+ const DWARFUnit &TargetCU, unsigned Flags,
+ MCSymbol &InstrLabel,
const DWARFDebugLine::Row &CurrentRow) {
- const uint64_t TargetUnitIndex = TargetCU->getOffset();
+ const uint64_t TargetUnitIndex = TargetCU.getOffset();
unsigned TargetFilenum = CurrentRow.File;
const uint32_t CurrentUnitIndex = RowReference.DwCompileUnitIndex;
// If the CU id from the current instruction location does not
@@ -711,7 +711,7 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
.getMCLineSections()
.getMCLineEntries();
const auto *It = MapLineEntries.find(Streamer.getCurrentSectionOnly());
- auto NewLineEntry = MCDwarfLineEntry(InstrLabel, DwarfLoc);
+ auto NewLineEntry = MCDwarfLineEntry(&InstrLabel, DwarfLoc);
// Check if line table exists and has entries before doing comparison
if (It != MapLineEntries.end() && !It->second.empty()) {
@@ -750,21 +750,17 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
if (FirstInstr)
Flags |= DWARF2_FLAG_IS_STMT;
const auto &FunctionDwarfUnits = BF.getDWARFUnits();
- const auto *It = std::find_if(
- FunctionDwarfUnits.begin(), FunctionDwarfUnits.end(),
- [RowReference](const DWARFUnit *Unit) {
- return Unit->getOffset() == RowReference.DwCompileUnitIndex;
- });
+ auto It = FunctionDwarfUnits.find(RowReference.DwCompileUnitIndex);
if (It != FunctionDwarfUnits.end()) {
- addToLineTable(RowReference, *It, Flags, InstrLabel, CurrentRow);
+ addToLineTable(RowReference, *It->second, Flags, *InstrLabel, CurrentRow);
continue;
}
// This rows is from CU that did not contain the original function.
// This might happen if BOLT moved/inlined that instruction from other CUs.
// In this case, we need to insert it to all CUs that the function
// originally beloned to.
- for (const DWARFUnit *Unit : BF.getDWARFUnits()) {
- addToLineTable(RowReference, Unit, Flags, InstrLabel, CurrentRow);
+ for (const auto &[_, Unit] : BF.getDWARFUnits()) {
+ addToLineTable(RowReference, *Unit, Flags, *InstrLabel, CurrentRow);
}
}
@@ -1148,7 +1144,7 @@ void BinaryEmitter::emitDebugLineInfoForOriginalFunctions() {
continue;
// Loop through all CUs in the function
- for (DWARFUnit *Unit : Function.getDWARFUnits()) {
+ for (const auto &[_, Unit] : Function.getDWARFUnits()) {
const DWARFDebugLine::LineTable *LineTable =
Function.getDWARFLineTableForUnit(Unit);
if (!LineTable)
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index b79f17919c4dc..578a87dc6c09d 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1490,7 +1490,7 @@ Error BinaryFunction::disassemble() {
add_instruction:
if (!getDWARFUnits().empty()) {
SmallVector<DebugLineTableRowRef, 1> Rows;
- for (DWARFUnit *Unit : getDWARFUnits()) {
+ for (const auto &[_, Unit] : getDWARFUnits()) {
const DWARFDebugLine::LineTable *LineTable =
getDWARFLineTableForUnit(Unit);
if (!LineTable)
>From 1c083718497f896af0930ff90ed0cbf9aa70f92d Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Wed, 3 Sep 2025 15:46:06 -0700
Subject: [PATCH 09/12] Rebased and addressed comments
---
bolt/include/bolt/Core/DebugData.h | 11 +++++++++++
bolt/lib/Core/BinaryContext.cpp | 2 +-
bolt/lib/Core/BinaryEmitter.cpp | 20 ++++++++++----------
bolt/test/perf2bolt/Inputs/perf_test.lds | 11 ++++++-----
4 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index 4c4f4d478b5e5..814978965ce3a 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -858,7 +858,14 @@ class ClusteredRows {
ArrayRef<DebugLineTableRowRef> getRows() const {
return ArrayRef<DebugLineTableRowRef>(beginPtrConst(), Size);
}
+
+ /// Returns the number of elements in the array.
uint64_t size() const { return Size; }
+
+ /// We re-purpose SMLoc inside MCInst to store the pointer
+ /// to ClusteredRows. fromSMLoc() and toSMLoc() are helper
+ /// functions to convert between SMLoc and ClusteredRows.
+
static const ClusteredRows *fromSMLoc(const SMLoc &Loc) {
return reinterpret_cast<const ClusteredRows *>(Loc.getPointer());
}
@@ -866,6 +873,8 @@ class ClusteredRows {
return SMLoc::getFromPointer(reinterpret_cast<const char *>(this));
}
+ /// Given a vector of DebugLineTableRowRef, this method
+ /// copies the elements into pre-allocated memory.
template <typename T> void populate(const T Vec) {
assert(Vec.size() == Size && "Sizes must match");
DebugLineTableRowRef *CurRawPtr = beginPtr();
@@ -880,6 +889,8 @@ class ClusteredRows {
DebugLineTableRowRef Rows;
ClusteredRows(uint64_t Size) : Size(Size) {}
+
+ /// Total size of the object including the array.
static uint64_t getTotalSize(uint64_t Size) {
assert(Size > 0 && "Size must be greater than 0");
return sizeof(ClusteredRows) + (Size - 1) * sizeof(DebugLineTableRowRef);
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 47ed62a53e3a8..e69f0c01d3d20 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1698,7 +1698,7 @@ void BinaryContext::preprocessDebugInfo() {
}
Function.addDWARFUnit(It->Unit);
- // Go forward and add all units from ranges that cover the function
+ // Go forward and add all units from ranges that cover the function.
while (++It != AllRanges.end()) {
if (It->LowPC > FunctionAddress || FunctionAddress >= It->HighPC)
break;
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index 8e5683ffa9831..7aaf721da9769 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -178,7 +178,7 @@ class BinaryEmitter {
/// marker in the line table program, but provides one to the DWARF generator
/// when it needs it.
void emitLineInfoEnd(const BinaryFunction &BF, MCSymbol *FunctionEndSymbol,
- DWARFUnit *Unit);
+ const DWARFUnit &Unit);
/// Emit debug line info for unprocessed functions from CUs that include
/// emitted functions.
@@ -439,7 +439,7 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function,
if (opts::UpdateDebugSections && !Function.getDWARFUnits().empty())
for (const auto &[_, Unit] : Function.getDWARFUnits())
- emitLineInfoEnd(Function, EndSymbol, Unit);
+ emitLineInfoEnd(Function, EndSymbol, *Unit);
// Exception handling info for the function.
emitLSDA(Function, FF);
@@ -707,13 +707,14 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
CurrentRow.Discriminator);
const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc();
BC.Ctx->clearDwarfLocSeen();
- auto &MapLineEntries = BC.getDwarfLineTable(TargetUnitIndex)
- .getMCLineSections()
- .getMCLineEntries();
+ const MCLineSection::MCLineDivisionMap &MapLineEntries =
+ BC.getDwarfLineTable(TargetUnitIndex)
+ .getMCLineSections()
+ .getMCLineEntries();
const auto *It = MapLineEntries.find(Streamer.getCurrentSectionOnly());
- auto NewLineEntry = MCDwarfLineEntry(&InstrLabel, DwarfLoc);
+ MCDwarfLineEntry NewLineEntry = MCDwarfLineEntry(&InstrLabel, DwarfLoc);
- // Check if line table exists and has entries before doing comparison
+ // Check if line table exists and has entries before doing comparison.
if (It != MapLineEntries.end() && !It->second.empty()) {
// Check if the new line entry has the same debug info as the last one
// to avoid duplicates. We don't compare labels since different
@@ -769,12 +770,11 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
void BinaryEmitter::emitLineInfoEnd(const BinaryFunction &BF,
MCSymbol *FunctionEndLabel,
- DWARFUnit *Unit) {
- assert(Unit && "DWARF unit expected");
+ const DWARFUnit &Unit) {
BC.Ctx->setCurrentDwarfLoc(0, 0, 0, DWARF2_FLAG_END_SEQUENCE, 0, 0);
const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc();
BC.Ctx->clearDwarfLocSeen();
- BC.getDwarfLineTable(Unit->getOffset())
+ BC.getDwarfLineTable(Unit.getOffset())
.getMCLineSections()
.addLineEntry(MCDwarfLineEntry(FunctionEndLabel, DwarfLoc),
Streamer.getCurrentSectionOnly());
diff --git a/bolt/test/perf2bolt/Inputs/perf_test.lds b/bolt/test/perf2bolt/Inputs/perf_test.lds
index c2704d73a638c..66d925a05bebc 100644
--- a/bolt/test/perf2bolt/Inputs/perf_test.lds
+++ b/bolt/test/perf2bolt/Inputs/perf_test.lds
@@ -1,12 +1,13 @@
SECTIONS {
- . = 0x400000 + SIZEOF_HEADERS;
+ . = SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
+ . = 0x212e8;
.dynsym : { *(.dynsym) }
- . = 0x801000;
+ . = 0x31860;
.text : { *(.text*) }
- . = 0x803000;
+ . = 0x41c20;
.fini_array : { *(.fini_array) }
- . = 0x805000;
+ . = 0x54e18;
.data : { *(.data) }
-}
+}
\ No newline at end of file
>From e80b4aaaf0d4bf16090168091f24d5f75f4debd9 Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Wed, 10 Sep 2025 16:32:27 -0700
Subject: [PATCH 10/12] Revert formatting change
---
bolt/test/lit.cfg.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/bolt/test/lit.cfg.py b/bolt/test/lit.cfg.py
index 0f2d3b1aa0eaf..3299051db4983 100644
--- a/bolt/test/lit.cfg.py
+++ b/bolt/test/lit.cfg.py
@@ -10,7 +10,8 @@
import lit.util
from lit.llvm import llvm_config
-from lit.llvm.subst import FindTool, ToolSubst
+from lit.llvm.subst import ToolSubst
+from lit.llvm.subst import FindTool
# Configuration file for the 'lit' test runner.
>From c3a6c8bfa58535691399721cb72a7ead1be79279 Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Wed, 10 Sep 2025 18:16:01 -0700
Subject: [PATCH 11/12] Converted test to X86 asm
---
bolt/test/Inputs/multi-cu-common.h | 10 -
bolt/test/Inputs/multi-cu-file1.c | 9 -
bolt/test/Inputs/multi-cu-file2.c | 8 -
bolt/test/X86/multi-cu-debug-line.s | 827 ++++++++++++++++++++++++++++
bolt/test/multi-cu-debug-line.test | 108 ----
5 files changed, 827 insertions(+), 135 deletions(-)
delete mode 100644 bolt/test/Inputs/multi-cu-common.h
delete mode 100644 bolt/test/Inputs/multi-cu-file1.c
delete mode 100644 bolt/test/Inputs/multi-cu-file2.c
create mode 100644 bolt/test/X86/multi-cu-debug-line.s
delete mode 100644 bolt/test/multi-cu-debug-line.test
diff --git a/bolt/test/Inputs/multi-cu-common.h b/bolt/test/Inputs/multi-cu-common.h
deleted file mode 100644
index aeb8076305dce..0000000000000
--- a/bolt/test/Inputs/multi-cu-common.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef MULTI_CU_COMMON_H
-#define MULTI_CU_COMMON_H
-
-static inline int common_inline_function(int x) {
- int result = x * 2;
- result += 10;
- return result;
-}
-
-#endif // MULTI_CU_COMMON_H
diff --git a/bolt/test/Inputs/multi-cu-file1.c b/bolt/test/Inputs/multi-cu-file1.c
deleted file mode 100644
index f3528b2acddb8..0000000000000
--- a/bolt/test/Inputs/multi-cu-file1.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "multi-cu-common.h"
-#include <stdio.h>
-
-int main() {
- int value = 5;
- int result = common_inline_function(value);
- printf("File1: Result is %d\n", result);
- return 0;
-}
diff --git a/bolt/test/Inputs/multi-cu-file2.c b/bolt/test/Inputs/multi-cu-file2.c
deleted file mode 100644
index f33af72595afe..0000000000000
--- a/bolt/test/Inputs/multi-cu-file2.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "multi-cu-common.h"
-#include <stdio.h>
-
-void helper_function() {
- int value = 10;
- int result = common_inline_function(value);
- printf("File2: Helper result is %d\n", result);
-}
diff --git a/bolt/test/X86/multi-cu-debug-line.s b/bolt/test/X86/multi-cu-debug-line.s
new file mode 100644
index 0000000000000..b3e9be3d9ce42
--- /dev/null
+++ b/bolt/test/X86/multi-cu-debug-line.s
@@ -0,0 +1,827 @@
+## Test that BOLT correctly handles debug line information for functions
+## that belong to multiple compilation units (e.g., inline functions in
+## common header files). This is the assembly version of the multi-cu-debug-line.test.
+## The test covers two scenarios:
+## 1. Normal processing: .debug_line section shows lines for the function
+## in all CUs where it was compiled, with no duplicate rows within CUs
+## 2. Functions not processed: When BOLT doesn't process functions (using
+## --funcs with nonexistent function), original debug info is preserved
+
+# REQUIRES: system-linux
+
+## Split the input into separate object files
+# RUN: split-file %s %t
+
+## Assemble the separate object files
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/multi-cu-file1.s -o %t/multi-cu-file1.o
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/multi-cu-file2.s -o %t/multi-cu-file2.o
+
+## Link them together with debug info
+# RUN: %clang %cflags %t/multi-cu-file1.o %t/multi-cu-file2.o -o %t.exe -Wl,-q
+
+## Test 1: Normal BOLT processing (functions are processed/optimized)
+# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
+# RUN: llvm-dwarfdump --debug-line %t.bolt > %t.debug-line.txt
+# RUN: FileCheck %s --check-prefix=BASIC --input-file %t.debug-line.txt
+
+## Check that debug line information is present for both compilation units
+# BASIC: debug_line[{{.*}}]
+# BASIC: file_names[{{.*}}]:
+# BASIC: name: "{{.*}}multi-cu-file1.c"
+# BASIC: debug_line[{{.*}}]
+# BASIC: file_names[{{.*}}]:
+# BASIC: name: "{{.*}}multi-cu-file2.c"
+
+## Use our helper script to create a normalized table without addresses
+# RUN: process-debug-line %t.debug-line.txt > %t.normalized-debug-line.txt
+# RUN: FileCheck %s --check-prefix=NORMALIZED --input-file %t.normalized-debug-line.txt
+
+## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
+## in both compilation units
+# NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h
+# NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h
+
+## Verify that we have line entries for the inline function in multiple CUs
+## by checking that the header file appears multiple times in different contexts
+# RUN: grep -c "multi-cu-common.h" %t.debug-line.txt > %t.header-count.txt
+# RUN: FileCheck %s --check-prefix=MULTI-CU --input-file %t.header-count.txt
+
+## The header should appear in debug line info for multiple CUs
+# MULTI-CU: {{[2-9]|[1-9][0-9]+}}
+
+## Check that there are no duplicate line table rows within the same CU
+## This verifies the fix for the bug where duplicate entries were created
+# RUN: sort %t.normalized-debug-line.txt | uniq -c | \
+# RUN: awk '$1 > 1 {print "DUPLICATE_ROW: " $0}' > %t.duplicates.txt
+# RUN: FileCheck %s --check-prefix=NO-DUPLICATES --input-file %t.duplicates.txt --allow-empty
+
+## Should have no duplicate normalized rows (file should be empty)
+## Note: Cross-CU duplicates are expected and valid (same function in different CUs)
+## but within-CU duplicates would indicate a bug
+# NO-DUPLICATES-NOT: DUPLICATE_ROW
+
+## Test 2: Functions not processed by BOLT (using --funcs with nonexistent function)
+## This tests the code path where BOLT preserves original debug info
+# RUN: llvm-bolt %t.exe -o %t.not-emitted.bolt --update-debug-sections --funcs=nonexistent_function
+# RUN: llvm-dwarfdump --debug-line %t.not-emitted.bolt > %t.not-emitted.debug-line.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-BASIC --input-file %t.not-emitted.debug-line.txt
+
+## Check that debug line information is still present for both compilation units when functions aren't processed
+# PRESERVED-BASIC: debug_line[{{.*}}]
+# PRESERVED-BASIC: file_names[{{.*}}]:
+# PRESERVED-BASIC: name: "{{.*}}multi-cu-file1.c"
+# PRESERVED-BASIC: debug_line[{{.*}}]
+# PRESERVED-BASIC: file_names[{{.*}}]:
+# PRESERVED-BASIC: name: "{{.*}}multi-cu-file2.c"
+
+## Create normalized output for the not-emitted case
+# RUN: process-debug-line %t.not-emitted.debug-line.txt > %t.not-emitted.normalized.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-NORMALIZED --input-file %t.not-emitted.normalized.txt
+
+## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
+## in both compilation units (preserved from original)
+# PRESERVED-NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h
+# PRESERVED-NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h
+
+## Verify that we have line entries for the inline function in multiple CUs (preserved)
+## by checking that the header file appears multiple times in different contexts
+# RUN: grep -c "multi-cu-common.h" %t.not-emitted.debug-line.txt > %t.preserved-header-count.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-MULTI-CU --input-file %t.preserved-header-count.txt
+
+## The header should appear in debug line info for multiple CUs (preserved from original)
+# PRESERVED-MULTI-CU: {{[2-9]|[1-9][0-9]+}}
+
+## Check that original debug info is preserved for main functions
+# RUN: grep "multi-cu-file1.c.*multi-cu-file1.c" %t.not-emitted.normalized.txt > %t.preserved-main.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-MAIN --input-file %t.preserved-main.txt
+
+# PRESERVED-MAIN: multi-cu-file1.c {{[0-9]+}} {{[0-9]+}} multi-cu-file1.c
+
+## Check that original debug info is preserved for file2 functions
+# RUN: grep "multi-cu-file2.c.*multi-cu-file2.c" %t.not-emitted.normalized.txt > %t.preserved-file2.txt
+# RUN: FileCheck %s --check-prefix=PRESERVED-FILE2 --input-file %t.preserved-file2.txt
+
+# PRESERVED-FILE2: multi-cu-file2.c {{[0-9]+}} {{[0-9]+}} multi-cu-file2.c
+
+## Note: We do not check for duplicates in Test 2 since we are preserving original debug info as-is
+## and the original may contain patterns that would be flagged as duplicates by our normalization
+
+;--- multi-cu-file1.s
+ .file "multi-cu-file1.c"
+ .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file1.c"
+ .text
+ .globl main # -- Begin function main
+ .p2align 4
+ .type main, at function
+main: # @main
+.Lfunc_begin0:
+ .loc 1 4 0 # bolt/test/Inputs/multi-cu-file1.c:4:0
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+ movl $0, -4(%rbp)
+.Ltmp0:
+ .loc 1 5 7 prologue_end # bolt/test/Inputs/multi-cu-file1.c:5:7
+ movl $5, -8(%rbp)
+ .loc 1 6 39 # bolt/test/Inputs/multi-cu-file1.c:6:39
+ movl -8(%rbp), %edi
+ .loc 1 6 16 is_stmt 0 # bolt/test/Inputs/multi-cu-file1.c:6:16
+ callq common_inline_function
+ .loc 1 6 7 # bolt/test/Inputs/multi-cu-file1.c:6:7
+ movl %eax, -12(%rbp)
+ .loc 1 7 35 is_stmt 1 # bolt/test/Inputs/multi-cu-file1.c:7:35
+ movl -12(%rbp), %esi
+ .loc 1 7 3 is_stmt 0 # bolt/test/Inputs/multi-cu-file1.c:7:3
+ leaq .L.str(%rip), %rdi
+ movb $0, %al
+ callq printf
+ .loc 1 8 3 is_stmt 1 # bolt/test/Inputs/multi-cu-file1.c:8:3
+ xorl %eax, %eax
+ .loc 1 8 3 epilogue_begin is_stmt 0 # bolt/test/Inputs/multi-cu-file1.c:8:3
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp1:
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .cfi_endproc
+ # -- End function
+ .p2align 4 # -- Begin function common_inline_function
+ .type common_inline_function, at function
+common_inline_function: # @common_inline_function
+.Lfunc_begin1:
+ .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h"
+ .loc 2 4 0 is_stmt 1 # bolt/test/Inputs/multi-cu-common.h:4:0
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+.Ltmp2:
+ .loc 2 5 16 prologue_end # bolt/test/Inputs/multi-cu-common.h:5:16
+ movl -4(%rbp), %eax
+ .loc 2 5 18 is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:5:18
+ shll %eax
+ .loc 2 5 7 # bolt/test/Inputs/multi-cu-common.h:5:7
+ movl %eax, -8(%rbp)
+ .loc 2 6 10 is_stmt 1 # bolt/test/Inputs/multi-cu-common.h:6:10
+ movl -8(%rbp), %eax
+ addl $10, %eax
+ movl %eax, -8(%rbp)
+ .loc 2 7 10 # bolt/test/Inputs/multi-cu-common.h:7:10
+ movl -8(%rbp), %eax
+ .loc 2 7 3 epilogue_begin is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:7:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp3:
+.Lfunc_end1:
+ .size common_inline_function, .Lfunc_end1-common_inline_function
+ .cfi_endproc
+ # -- End function
+ .type .L.str, at object # @.str
+ .section .rodata.str1.1,"aMS", at progbits,1
+.L.str:
+ .asciz "File1: Result is %d\n"
+ .size .L.str, 21
+
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 14 # DW_FORM_strp
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 1 # DW_TAG_array_type
+ .byte 1 # DW_CHILDREN_yes
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 33 # DW_TAG_subrange_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 55 # DW_AT_count
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0xbe DW_TAG_compile_unit
+ .long .Linfo_string0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .long .Linfo_string1 # DW_AT_name
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .long .Linfo_string2 # DW_AT_comp_dir
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 2 # Abbrev [2] 0x2a:0x11 DW_TAG_variable
+ .long 59 # DW_AT_type
+ .byte 1 # DW_AT_decl_file
+ .byte 7 # DW_AT_decl_line
+ .byte 9 # DW_AT_location
+ .byte 3
+ .quad .L.str
+ .byte 3 # Abbrev [3] 0x3b:0xc DW_TAG_array_type
+ .long 71 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x40:0x6 DW_TAG_subrange_type
+ .long 78 # DW_AT_type
+ .byte 21 # DW_AT_count
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x47:0x7 DW_TAG_base_type
+ .long .Linfo_string3 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 6 # Abbrev [6] 0x4e:0x7 DW_TAG_base_type
+ .long .Linfo_string4 # DW_AT_name
+ .byte 8 # DW_AT_byte_size
+ .byte 7 # DW_AT_encoding
+ .byte 7 # Abbrev [7] 0x55:0x36 DW_TAG_subprogram
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long .Linfo_string5 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 193 # DW_AT_type
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x6e:0xe DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 120
+ .long .Linfo_string8 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 193 # DW_AT_type
+ .byte 8 # Abbrev [8] 0x7c:0xe DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 116
+ .long .Linfo_string9 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 193 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x8b:0x36 DW_TAG_subprogram
+ .quad .Lfunc_begin1 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long .Linfo_string7 # DW_AT_name
+ .byte 2 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 193 # DW_AT_type
+ .byte 10 # Abbrev [10] 0xa4:0xe DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long .Linfo_string10 # DW_AT_name
+ .byte 2 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 193 # DW_AT_type
+ .byte 8 # Abbrev [8] 0xb2:0xe DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 120
+ .long .Linfo_string9 # DW_AT_name
+ .byte 2 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 193 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0xc1:0x7 DW_TAG_base_type
+ .long .Linfo_string6 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 18.0.0" # string offset=0
+.Linfo_string1:
+ .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file1.c" # string offset=43
+.Linfo_string2:
+ .asciz "/repo/llvm-project" # string offset=125
+.Linfo_string3:
+ .asciz "char" # string offset=173
+.Linfo_string4:
+ .asciz "__ARRAY_SIZE_TYPE__" # string offset=178
+.Linfo_string5:
+ .asciz "main" # string offset=198
+.Linfo_string6:
+ .asciz "int" # string offset=203
+.Linfo_string7:
+ .asciz "common_inline_function" # string offset=207
+.Linfo_string8:
+ .asciz "value" # string offset=230
+.Linfo_string9:
+ .asciz "result" # string offset=236
+.Linfo_string10:
+ .asciz "x" # string offset=243
+ .ident "clang version 18.0.0"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym common_inline_function
+ .addrsig_sym printf
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
+
+;--- multi-cu-file2.s
+ .file "multi-cu-file2.c"
+ .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file2.c"
+ .text
+ .globl helper_function # -- Begin function helper_function
+ .p2align 4
+ .type helper_function, at function
+helper_function: # @helper_function
+.Lfunc_begin0:
+ .loc 1 4 0 # bolt/test/Inputs/multi-cu-file2.c:4:0
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+.Ltmp0:
+ .loc 1 5 7 prologue_end # bolt/test/Inputs/multi-cu-file2.c:5:7
+ movl $10, -4(%rbp)
+ .loc 1 6 39 # bolt/test/Inputs/multi-cu-file2.c:6:39
+ movl -4(%rbp), %edi
+ .loc 1 6 16 is_stmt 0 # bolt/test/Inputs/multi-cu-file2.c:6:16
+ callq common_inline_function
+ .loc 1 6 7 # bolt/test/Inputs/multi-cu-file2.c:6:7
+ movl %eax, -8(%rbp)
+ .loc 1 7 42 is_stmt 1 # bolt/test/Inputs/multi-cu-file2.c:7:42
+ movl -8(%rbp), %esi
+ .loc 1 7 3 is_stmt 0 # bolt/test/Inputs/multi-cu-file2.c:7:3
+ leaq .L.str(%rip), %rdi
+ movb $0, %al
+ callq printf
+ .loc 1 8 1 epilogue_begin is_stmt 1 # bolt/test/Inputs/multi-cu-file2.c:8:1
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp1:
+.Lfunc_end0:
+ .size helper_function, .Lfunc_end0-helper_function
+ .cfi_endproc
+ # -- End function
+ .p2align 4 # -- Begin function common_inline_function
+ .type common_inline_function, at function
+common_inline_function: # @common_inline_function
+.Lfunc_begin1:
+ .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h"
+ .loc 2 4 0 # bolt/test/Inputs/multi-cu-common.h:4:0
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+.Ltmp2:
+ .loc 2 5 16 prologue_end # bolt/test/Inputs/multi-cu-common.h:5:16
+ movl -4(%rbp), %eax
+ .loc 2 5 18 is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:5:18
+ shll %eax
+ .loc 2 5 7 # bolt/test/Inputs/multi-cu-common.h:5:7
+ movl %eax, -8(%rbp)
+ .loc 2 6 10 is_stmt 1 # bolt/test/Inputs/multi-cu-common.h:6:10
+ movl -8(%rbp), %eax
+ addl $10, %eax
+ movl %eax, -8(%rbp)
+ .loc 2 7 10 # bolt/test/Inputs/multi-cu-common.h:7:10
+ movl -8(%rbp), %eax
+ .loc 2 7 3 epilogue_begin is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:7:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp3:
+.Lfunc_end1:
+ .size common_inline_function, .Lfunc_end1-common_inline_function
+ .cfi_endproc
+ # -- End function
+ .type .L.str, at object # @.str
+ .section .rodata.str1.1,"aMS", at progbits,1
+.L.str:
+ .asciz "File2: Helper result is %d\n"
+ .size .L.str, 28
+
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 14 # DW_FORM_strp
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 1 # DW_TAG_array_type
+ .byte 1 # DW_CHILDREN_yes
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 33 # DW_TAG_subrange_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 55 # DW_AT_count
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0xba DW_TAG_compile_unit
+ .long .Linfo_string0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .long .Linfo_string1 # DW_AT_name
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .long .Linfo_string2 # DW_AT_comp_dir
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 2 # Abbrev [2] 0x2a:0x11 DW_TAG_variable
+ .long 59 # DW_AT_type
+ .byte 1 # DW_AT_decl_file
+ .byte 7 # DW_AT_decl_line
+ .byte 9 # DW_AT_location
+ .byte 3
+ .quad .L.str
+ .byte 3 # Abbrev [3] 0x3b:0xc DW_TAG_array_type
+ .long 71 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x40:0x6 DW_TAG_subrange_type
+ .long 78 # DW_AT_type
+ .byte 28 # DW_AT_count
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x47:0x7 DW_TAG_base_type
+ .long .Linfo_string3 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 6 # Abbrev [6] 0x4e:0x7 DW_TAG_base_type
+ .long .Linfo_string4 # DW_AT_name
+ .byte 8 # DW_AT_byte_size
+ .byte 7 # DW_AT_encoding
+ .byte 7 # Abbrev [7] 0x55:0x32 DW_TAG_subprogram
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long .Linfo_string5 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x6a:0xe DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long .Linfo_string8 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 189 # DW_AT_type
+ .byte 8 # Abbrev [8] 0x78:0xe DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 120
+ .long .Linfo_string9 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 189 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x87:0x36 DW_TAG_subprogram
+ .quad .Lfunc_begin1 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long .Linfo_string6 # DW_AT_name
+ .byte 2 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 189 # DW_AT_type
+ .byte 10 # Abbrev [10] 0xa0:0xe DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long .Linfo_string10 # DW_AT_name
+ .byte 2 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 189 # DW_AT_type
+ .byte 8 # Abbrev [8] 0xae:0xe DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 120
+ .long .Linfo_string9 # DW_AT_name
+ .byte 2 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 189 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0xbd:0x7 DW_TAG_base_type
+ .long .Linfo_string7 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 18.0.0" # string offset=0
+.Linfo_string1:
+ .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file2.c" # string offset=43
+.Linfo_string2:
+ .asciz "/repo/llvm-project" # string offset=125
+.Linfo_string3:
+ .asciz "char" # string offset=173
+.Linfo_string4:
+ .asciz "__ARRAY_SIZE_TYPE__" # string offset=178
+.Linfo_string5:
+ .asciz "helper_function" # string offset=198
+.Linfo_string6:
+ .asciz "common_inline_function" # string offset=214
+.Linfo_string7:
+ .asciz "int" # string offset=237
+.Linfo_string8:
+ .asciz "value" # string offset=241
+.Linfo_string9:
+ .asciz "result" # string offset=247
+.Linfo_string10:
+ .asciz "x" # string offset=254
+ .ident "clang version 18.0.0"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym common_inline_function
+ .addrsig_sym printf
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/bolt/test/multi-cu-debug-line.test b/bolt/test/multi-cu-debug-line.test
deleted file mode 100644
index a94c901bbcc5a..0000000000000
--- a/bolt/test/multi-cu-debug-line.test
+++ /dev/null
@@ -1,108 +0,0 @@
-## Test that BOLT correctly handles debug line information for functions
-## that belong to multiple compilation units (e.g., inline functions in
-## common header files). The test covers two scenarios:
-## 1. Normal processing: .debug_line section shows lines for the function
-## in all CUs where it was compiled, with no duplicate rows within CUs
-## 2. Functions not processed: When BOLT doesn't process functions (using
-## --funcs with nonexistent function), original debug info is preserved
-
-# REQUIRES: system-linux
-
-## Compile test files with debug info
-# RUN: %clang %cflags -O0 -g %S/Inputs/multi-cu-file1.c %S/Inputs/multi-cu-file2.c \
-# RUN: -I%S/Inputs -o %t.exe -Wl,-q
-
-## Test 1: Normal BOLT processing (functions are processed/optimized)
-# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
-# RUN: llvm-dwarfdump --debug-line %t.bolt > %t.debug-line.txt
-# RUN: FileCheck %s --check-prefix=BASIC --input-file %t.debug-line.txt
-
-## Check that debug line information is present for both compilation units
-# BASIC: debug_line[{{.*}}]
-# BASIC: file_names[{{.*}}]:
-# BASIC: name: "{{.*}}multi-cu-file1.c"
-# BASIC: debug_line[{{.*}}]
-# BASIC: file_names[{{.*}}]:
-# BASIC: name: "{{.*}}multi-cu-file2.c"
-
-## Use our helper script to create a normalized table without addresses
-# RUN: process-debug-line %t.debug-line.txt > %t.normalized-debug-line.txt
-# RUN: FileCheck %s --check-prefix=NORMALIZED --input-file %t.normalized-debug-line.txt
-
-## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
-## in both compilation units
-# NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h
-# NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h
-# NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h
-# NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h
-# NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h
-# NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h
-
-## Verify that we have line entries for the inline function in multiple CUs
-## by checking that the header file appears multiple times in different contexts
-# RUN: grep -c "multi-cu-common.h" %t.debug-line.txt > %t.header-count.txt
-# RUN: FileCheck %s --check-prefix=MULTI-CU --input-file %t.header-count.txt
-
-## The header should appear in debug line info for multiple CUs
-# MULTI-CU: {{[2-9]|[1-9][0-9]+}}
-
-## Check that there are no duplicate line table rows within the same CU
-## This verifies the fix for the bug where duplicate entries were created
-# RUN: sort %t.normalized-debug-line.txt | uniq -c | \
-# RUN: awk '$1 > 1 {print "DUPLICATE_ROW: " $0}' > %t.duplicates.txt
-# RUN: FileCheck %s --check-prefix=NO-DUPLICATES --input-file %t.duplicates.txt --allow-empty
-
-## Should have no duplicate normalized rows (file should be empty)
-## Note: Cross-CU duplicates are expected and valid (same function in different CUs)
-## but within-CU duplicates would indicate a bug
-# NO-DUPLICATES-NOT: DUPLICATE_ROW
-
-## Test 2: Functions not processed by BOLT (using --funcs with nonexistent function)
-## This tests the code path where BOLT preserves original debug info
-# RUN: llvm-bolt %t.exe -o %t.not-emitted.bolt --update-debug-sections --funcs=nonexistent_function
-# RUN: llvm-dwarfdump --debug-line %t.not-emitted.bolt > %t.not-emitted.debug-line.txt
-# RUN: FileCheck %s --check-prefix=PRESERVED-BASIC --input-file %t.not-emitted.debug-line.txt
-
-## Check that debug line information is still present for both compilation units when functions aren't processed
-# PRESERVED-BASIC: debug_line[{{.*}}]
-# PRESERVED-BASIC: file_names[{{.*}}]:
-# PRESERVED-BASIC: name: "{{.*}}multi-cu-file1.c"
-# PRESERVED-BASIC: debug_line[{{.*}}]
-# PRESERVED-BASIC: file_names[{{.*}}]:
-# PRESERVED-BASIC: name: "{{.*}}multi-cu-file2.c"
-
-## Create normalized output for the not-emitted case
-# RUN: process-debug-line %t.not-emitted.debug-line.txt > %t.not-emitted.normalized.txt
-# RUN: FileCheck %s --check-prefix=PRESERVED-NORMALIZED --input-file %t.not-emitted.normalized.txt
-
-## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h
-## in both compilation units (preserved from original)
-# PRESERVED-NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h
-# PRESERVED-NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h
-# PRESERVED-NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h
-# PRESERVED-NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h
-# PRESERVED-NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h
-# PRESERVED-NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h
-
-## Verify that we have line entries for the inline function in multiple CUs (preserved)
-## by checking that the header file appears multiple times in different contexts
-# RUN: grep -c "multi-cu-common.h" %t.not-emitted.debug-line.txt > %t.preserved-header-count.txt
-# RUN: FileCheck %s --check-prefix=PRESERVED-MULTI-CU --input-file %t.preserved-header-count.txt
-
-## The header should appear in debug line info for multiple CUs (preserved from original)
-# PRESERVED-MULTI-CU: {{[2-9]|[1-9][0-9]+}}
-
-## Check that original debug info is preserved for main functions
-# RUN: grep "multi-cu-file1.c.*multi-cu-file1.c" %t.not-emitted.normalized.txt > %t.preserved-main.txt
-# RUN: FileCheck %s --check-prefix=PRESERVED-MAIN --input-file %t.preserved-main.txt
-
-# PRESERVED-MAIN: multi-cu-file1.c {{[0-9]+}} {{[0-9]+}} multi-cu-file1.c
-
-## Check that original debug info is preserved for file2 functions
-# RUN: grep "multi-cu-file2.c.*multi-cu-file2.c" %t.not-emitted.normalized.txt > %t.preserved-file2.txt
-# RUN: FileCheck %s --check-prefix=PRESERVED-FILE2 --input-file %t.preserved-file2.txt
-
-# PRESERVED-FILE2: multi-cu-file2.c {{[0-9]+}} {{[0-9]+}} multi-cu-file2.c
-
-## Note: We do not check for duplicates in Test 2 since we are preserving original debug info as-is
-## and the original may contain patterns that would be flagged as duplicates by our normalization
>From 7552b8f4ff4de722c0936b70ca8679159d880ebf Mon Sep 17 00:00:00 2001
From: Grigory Pastukhov <gpastukhov at meta.com>
Date: Thu, 11 Sep 2025 09:54:58 -0700
Subject: [PATCH 12/12] Reduced assembly for the test case
---
bolt/test/X86/multi-cu-debug-line.s | 608 +++-------------------------
1 file changed, 54 insertions(+), 554 deletions(-)
diff --git a/bolt/test/X86/multi-cu-debug-line.s b/bolt/test/X86/multi-cu-debug-line.s
index b3e9be3d9ce42..15f49a211e58b 100644
--- a/bolt/test/X86/multi-cu-debug-line.s
+++ b/bolt/test/X86/multi-cu-debug-line.s
@@ -9,14 +9,9 @@
# REQUIRES: system-linux
-## Split the input into separate object files
# RUN: split-file %s %t
-
-## Assemble the separate object files
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/multi-cu-file1.s -o %t/multi-cu-file1.o
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/multi-cu-file2.s -o %t/multi-cu-file2.o
-
-## Link them together with debug info
# RUN: %clang %cflags %t/multi-cu-file1.o %t/multi-cu-file2.o -o %t.exe -Wl,-q
## Test 1: Normal BOLT processing (functions are processed/optimized)
@@ -111,96 +106,33 @@
# PRESERVED-FILE2: multi-cu-file2.c {{[0-9]+}} {{[0-9]+}} multi-cu-file2.c
-## Note: We do not check for duplicates in Test 2 since we are preserving original debug info as-is
-## and the original may contain patterns that would be flagged as duplicates by our normalization
-
;--- multi-cu-file1.s
- .file "multi-cu-file1.c"
- .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file1.c"
.text
- .globl main # -- Begin function main
- .p2align 4
+ .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file1.c"
+ .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h"
+
+ .globl main
.type main, at function
-main: # @main
+main:
.Lfunc_begin0:
- .loc 1 4 0 # bolt/test/Inputs/multi-cu-file1.c:4:0
- .cfi_startproc
-# %bb.0:
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- subq $16, %rsp
- movl $0, -4(%rbp)
-.Ltmp0:
- .loc 1 5 7 prologue_end # bolt/test/Inputs/multi-cu-file1.c:5:7
- movl $5, -8(%rbp)
- .loc 1 6 39 # bolt/test/Inputs/multi-cu-file1.c:6:39
- movl -8(%rbp), %edi
- .loc 1 6 16 is_stmt 0 # bolt/test/Inputs/multi-cu-file1.c:6:16
+ .loc 1 4 0
callq common_inline_function
- .loc 1 6 7 # bolt/test/Inputs/multi-cu-file1.c:6:7
- movl %eax, -12(%rbp)
- .loc 1 7 35 is_stmt 1 # bolt/test/Inputs/multi-cu-file1.c:7:35
- movl -12(%rbp), %esi
- .loc 1 7 3 is_stmt 0 # bolt/test/Inputs/multi-cu-file1.c:7:3
- leaq .L.str(%rip), %rdi
- movb $0, %al
- callq printf
- .loc 1 8 3 is_stmt 1 # bolt/test/Inputs/multi-cu-file1.c:8:3
- xorl %eax, %eax
- .loc 1 8 3 epilogue_begin is_stmt 0 # bolt/test/Inputs/multi-cu-file1.c:8:3
- addq $16, %rsp
- popq %rbp
- .cfi_def_cfa %rsp, 8
+ .loc 1 8 0
retq
-.Ltmp1:
.Lfunc_end0:
.size main, .Lfunc_end0-main
- .cfi_endproc
- # -- End function
- .p2align 4 # -- Begin function common_inline_function
+
.type common_inline_function, at function
-common_inline_function: # @common_inline_function
+common_inline_function:
.Lfunc_begin1:
- .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h"
- .loc 2 4 0 is_stmt 1 # bolt/test/Inputs/multi-cu-common.h:4:0
- .cfi_startproc
-# %bb.0:
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- movl %edi, -4(%rbp)
-.Ltmp2:
- .loc 2 5 16 prologue_end # bolt/test/Inputs/multi-cu-common.h:5:16
- movl -4(%rbp), %eax
- .loc 2 5 18 is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:5:18
- shll %eax
- .loc 2 5 7 # bolt/test/Inputs/multi-cu-common.h:5:7
- movl %eax, -8(%rbp)
- .loc 2 6 10 is_stmt 1 # bolt/test/Inputs/multi-cu-common.h:6:10
- movl -8(%rbp), %eax
+ .loc 2 5 0
+ movl $42, %eax
+ .loc 2 6 0
addl $10, %eax
- movl %eax, -8(%rbp)
- .loc 2 7 10 # bolt/test/Inputs/multi-cu-common.h:7:10
- movl -8(%rbp), %eax
- .loc 2 7 3 epilogue_begin is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:7:3
- popq %rbp
- .cfi_def_cfa %rsp, 8
+ .loc 2 7 0
retq
-.Ltmp3:
.Lfunc_end1:
.size common_inline_function, .Lfunc_end1-common_inline_function
- .cfi_endproc
- # -- End function
- .type .L.str, at object # @.str
- .section .rodata.str1.1,"aMS", at progbits,1
-.L.str:
- .asciz "File1: Result is %d\n"
- .size .L.str, 21
.section .debug_abbrev,"", at progbits
.byte 1 # Abbreviation Code
@@ -223,137 +155,30 @@ common_inline_function: # @common_inline_function
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 2 # Abbreviation Code
- .byte 52 # DW_TAG_variable
- .byte 0 # DW_CHILDREN_no
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 2 # DW_AT_location
- .byte 24 # DW_FORM_exprloc
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 3 # Abbreviation Code
- .byte 1 # DW_TAG_array_type
- .byte 1 # DW_CHILDREN_yes
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 4 # Abbreviation Code
- .byte 33 # DW_TAG_subrange_type
- .byte 0 # DW_CHILDREN_no
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 55 # DW_AT_count
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 5 # Abbreviation Code
- .byte 36 # DW_TAG_base_type
- .byte 0 # DW_CHILDREN_no
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 62 # DW_AT_encoding
- .byte 11 # DW_FORM_data1
- .byte 11 # DW_AT_byte_size
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 6 # Abbreviation Code
- .byte 36 # DW_TAG_base_type
- .byte 0 # DW_CHILDREN_no
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 11 # DW_AT_byte_size
- .byte 11 # DW_FORM_data1
- .byte 62 # DW_AT_encoding
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 7 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
- .byte 1 # DW_CHILDREN_yes
- .byte 17 # DW_AT_low_pc
- .byte 1 # DW_FORM_addr
- .byte 18 # DW_AT_high_pc
- .byte 6 # DW_FORM_data4
- .byte 64 # DW_AT_frame_base
- .byte 24 # DW_FORM_exprloc
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 63 # DW_AT_external
- .byte 25 # DW_FORM_flag_present
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 8 # Abbreviation Code
- .byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
- .byte 2 # DW_AT_location
- .byte 24 # DW_FORM_exprloc
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 9 # Abbreviation Code
- .byte 46 # DW_TAG_subprogram
- .byte 1 # DW_CHILDREN_yes
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 6 # DW_FORM_data4
- .byte 64 # DW_AT_frame_base
- .byte 24 # DW_FORM_exprloc
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
- .byte 39 # DW_AT_prototyped
- .byte 25 # DW_FORM_flag_present
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 10 # Abbreviation Code
- .byte 5 # DW_TAG_formal_parameter
- .byte 0 # DW_CHILDREN_no
- .byte 2 # DW_AT_location
- .byte 24 # DW_FORM_exprloc
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 0 # EOM(3)
+
.section .debug_info,"", at progbits
.Lcu_begin0:
- .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+ .long .Ldebug_info_end0-.Ldebug_info_start0
.Ldebug_info_start0:
.short 4 # DWARF version number
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
- .byte 1 # Abbrev [1] 0xb:0xbe DW_TAG_compile_unit
+ .byte 1 # Abbrev [1] 0xb:0x30 DW_TAG_compile_unit
.long .Linfo_string0 # DW_AT_producer
.short 29 # DW_AT_language
.long .Linfo_string1 # DW_AT_name
@@ -361,202 +186,63 @@ common_inline_function: # @common_inline_function
.long .Linfo_string2 # DW_AT_comp_dir
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc
- .byte 2 # Abbrev [2] 0x2a:0x11 DW_TAG_variable
- .long 59 # DW_AT_type
- .byte 1 # DW_AT_decl_file
- .byte 7 # DW_AT_decl_line
- .byte 9 # DW_AT_location
- .byte 3
- .quad .L.str
- .byte 3 # Abbrev [3] 0x3b:0xc DW_TAG_array_type
- .long 71 # DW_AT_type
- .byte 4 # Abbrev [4] 0x40:0x6 DW_TAG_subrange_type
- .long 78 # DW_AT_type
- .byte 21 # DW_AT_count
- .byte 0 # End Of Children Mark
- .byte 5 # Abbrev [5] 0x47:0x7 DW_TAG_base_type
- .long .Linfo_string3 # DW_AT_name
- .byte 6 # DW_AT_encoding
- .byte 1 # DW_AT_byte_size
- .byte 6 # Abbrev [6] 0x4e:0x7 DW_TAG_base_type
- .long .Linfo_string4 # DW_AT_name
- .byte 8 # DW_AT_byte_size
- .byte 7 # DW_AT_encoding
- .byte 7 # Abbrev [7] 0x55:0x36 DW_TAG_subprogram
+ .byte 2 # Abbrev [2] 0x2a:0x10 DW_TAG_subprogram
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
- .byte 1 # DW_AT_frame_base
- .byte 86
- .long .Linfo_string5 # DW_AT_name
+ .long .Linfo_string3 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 4 # DW_AT_decl_line
- .long 193 # DW_AT_type
- # DW_AT_external
- .byte 8 # Abbrev [8] 0x6e:0xe DW_TAG_variable
- .byte 2 # DW_AT_location
- .byte 145
- .byte 120
- .long .Linfo_string8 # DW_AT_name
- .byte 1 # DW_AT_decl_file
- .byte 5 # DW_AT_decl_line
- .long 193 # DW_AT_type
- .byte 8 # Abbrev [8] 0x7c:0xe DW_TAG_variable
- .byte 2 # DW_AT_location
- .byte 145
- .byte 116
- .long .Linfo_string9 # DW_AT_name
- .byte 1 # DW_AT_decl_file
- .byte 6 # DW_AT_decl_line
- .long 193 # DW_AT_type
- .byte 0 # End Of Children Mark
- .byte 9 # Abbrev [9] 0x8b:0x36 DW_TAG_subprogram
+ .byte 2 # Abbrev [2] 0x3a:0x10 DW_TAG_subprogram
.quad .Lfunc_begin1 # DW_AT_low_pc
.long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
- .byte 1 # DW_AT_frame_base
- .byte 86
- .long .Linfo_string7 # DW_AT_name
- .byte 2 # DW_AT_decl_file
- .byte 4 # DW_AT_decl_line
- # DW_AT_prototyped
- .long 193 # DW_AT_type
- .byte 10 # Abbrev [10] 0xa4:0xe DW_TAG_formal_parameter
- .byte 2 # DW_AT_location
- .byte 145
- .byte 124
- .long .Linfo_string10 # DW_AT_name
- .byte 2 # DW_AT_decl_file
- .byte 4 # DW_AT_decl_line
- .long 193 # DW_AT_type
- .byte 8 # Abbrev [8] 0xb2:0xe DW_TAG_variable
- .byte 2 # DW_AT_location
- .byte 145
- .byte 120
- .long .Linfo_string9 # DW_AT_name
+ .long .Linfo_string4 # DW_AT_name
.byte 2 # DW_AT_decl_file
.byte 5 # DW_AT_decl_line
- .long 193 # DW_AT_type
- .byte 0 # End Of Children Mark
- .byte 5 # Abbrev [5] 0xc1:0x7 DW_TAG_base_type
- .long .Linfo_string6 # DW_AT_name
- .byte 5 # DW_AT_encoding
- .byte 4 # DW_AT_byte_size
.byte 0 # End Of Children Mark
.Ldebug_info_end0:
+
.section .debug_str,"MS", at progbits,1
.Linfo_string0:
- .asciz "clang version 18.0.0" # string offset=0
+ .asciz "clang version 18.0.0"
.Linfo_string1:
- .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file1.c" # string offset=43
+ .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file1.c"
.Linfo_string2:
- .asciz "/repo/llvm-project" # string offset=125
+ .asciz "/repo/llvm-project"
.Linfo_string3:
- .asciz "char" # string offset=173
+ .asciz "main"
.Linfo_string4:
- .asciz "__ARRAY_SIZE_TYPE__" # string offset=178
-.Linfo_string5:
- .asciz "main" # string offset=198
-.Linfo_string6:
- .asciz "int" # string offset=203
-.Linfo_string7:
- .asciz "common_inline_function" # string offset=207
-.Linfo_string8:
- .asciz "value" # string offset=230
-.Linfo_string9:
- .asciz "result" # string offset=236
-.Linfo_string10:
- .asciz "x" # string offset=243
- .ident "clang version 18.0.0"
- .section ".note.GNU-stack","", at progbits
- .addrsig
- .addrsig_sym common_inline_function
- .addrsig_sym printf
+ .asciz "common_inline_function"
+
.section .debug_line,"", at progbits
.Lline_table_start0:
;--- multi-cu-file2.s
- .file "multi-cu-file2.c"
- .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file2.c"
.text
- .globl helper_function # -- Begin function helper_function
- .p2align 4
+ .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file2.c"
+ .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h"
+
+ .globl helper_function
.type helper_function, at function
-helper_function: # @helper_function
+helper_function:
.Lfunc_begin0:
- .loc 1 4 0 # bolt/test/Inputs/multi-cu-file2.c:4:0
- .cfi_startproc
-# %bb.0:
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- subq $16, %rsp
-.Ltmp0:
- .loc 1 5 7 prologue_end # bolt/test/Inputs/multi-cu-file2.c:5:7
- movl $10, -4(%rbp)
- .loc 1 6 39 # bolt/test/Inputs/multi-cu-file2.c:6:39
- movl -4(%rbp), %edi
- .loc 1 6 16 is_stmt 0 # bolt/test/Inputs/multi-cu-file2.c:6:16
+ .loc 1 4 0
callq common_inline_function
- .loc 1 6 7 # bolt/test/Inputs/multi-cu-file2.c:6:7
- movl %eax, -8(%rbp)
- .loc 1 7 42 is_stmt 1 # bolt/test/Inputs/multi-cu-file2.c:7:42
- movl -8(%rbp), %esi
- .loc 1 7 3 is_stmt 0 # bolt/test/Inputs/multi-cu-file2.c:7:3
- leaq .L.str(%rip), %rdi
- movb $0, %al
- callq printf
- .loc 1 8 1 epilogue_begin is_stmt 1 # bolt/test/Inputs/multi-cu-file2.c:8:1
- addq $16, %rsp
- popq %rbp
- .cfi_def_cfa %rsp, 8
+ .loc 1 8 0
retq
-.Ltmp1:
.Lfunc_end0:
.size helper_function, .Lfunc_end0-helper_function
- .cfi_endproc
- # -- End function
- .p2align 4 # -- Begin function common_inline_function
+
.type common_inline_function, at function
-common_inline_function: # @common_inline_function
+common_inline_function:
.Lfunc_begin1:
- .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h"
- .loc 2 4 0 # bolt/test/Inputs/multi-cu-common.h:4:0
- .cfi_startproc
-# %bb.0:
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- movl %edi, -4(%rbp)
-.Ltmp2:
- .loc 2 5 16 prologue_end # bolt/test/Inputs/multi-cu-common.h:5:16
- movl -4(%rbp), %eax
- .loc 2 5 18 is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:5:18
- shll %eax
- .loc 2 5 7 # bolt/test/Inputs/multi-cu-common.h:5:7
- movl %eax, -8(%rbp)
- .loc 2 6 10 is_stmt 1 # bolt/test/Inputs/multi-cu-common.h:6:10
- movl -8(%rbp), %eax
+ .loc 2 5 0
+ movl $42, %eax
+ .loc 2 6 0
addl $10, %eax
- movl %eax, -8(%rbp)
- .loc 2 7 10 # bolt/test/Inputs/multi-cu-common.h:7:10
- movl -8(%rbp), %eax
- .loc 2 7 3 epilogue_begin is_stmt 0 # bolt/test/Inputs/multi-cu-common.h:7:3
- popq %rbp
- .cfi_def_cfa %rsp, 8
+ .loc 2 7 0
retq
-.Ltmp3:
.Lfunc_end1:
.size common_inline_function, .Lfunc_end1-common_inline_function
- .cfi_endproc
- # -- End function
- .type .L.str, at object # @.str
- .section .rodata.str1.1,"aMS", at progbits,1
-.L.str:
- .asciz "File2: Helper result is %d\n"
- .size .L.str, 28
.section .debug_abbrev,"", at progbits
.byte 1 # Abbreviation Code
@@ -579,135 +265,30 @@ common_inline_function: # @common_inline_function
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 2 # Abbreviation Code
- .byte 52 # DW_TAG_variable
- .byte 0 # DW_CHILDREN_no
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 2 # DW_AT_location
- .byte 24 # DW_FORM_exprloc
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 3 # Abbreviation Code
- .byte 1 # DW_TAG_array_type
- .byte 1 # DW_CHILDREN_yes
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 4 # Abbreviation Code
- .byte 33 # DW_TAG_subrange_type
- .byte 0 # DW_CHILDREN_no
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 55 # DW_AT_count
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 5 # Abbreviation Code
- .byte 36 # DW_TAG_base_type
- .byte 0 # DW_CHILDREN_no
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 62 # DW_AT_encoding
- .byte 11 # DW_FORM_data1
- .byte 11 # DW_AT_byte_size
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 6 # Abbreviation Code
- .byte 36 # DW_TAG_base_type
- .byte 0 # DW_CHILDREN_no
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 11 # DW_AT_byte_size
- .byte 11 # DW_FORM_data1
- .byte 62 # DW_AT_encoding
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 7 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
- .byte 1 # DW_CHILDREN_yes
- .byte 17 # DW_AT_low_pc
- .byte 1 # DW_FORM_addr
- .byte 18 # DW_AT_high_pc
- .byte 6 # DW_FORM_data4
- .byte 64 # DW_AT_frame_base
- .byte 24 # DW_FORM_exprloc
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 63 # DW_AT_external
- .byte 25 # DW_FORM_flag_present
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 8 # Abbreviation Code
- .byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
- .byte 2 # DW_AT_location
- .byte 24 # DW_FORM_exprloc
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 9 # Abbreviation Code
- .byte 46 # DW_TAG_subprogram
- .byte 1 # DW_CHILDREN_yes
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 6 # DW_FORM_data4
- .byte 64 # DW_AT_frame_base
- .byte 24 # DW_FORM_exprloc
- .byte 3 # DW_AT_name
- .byte 14 # DW_FORM_strp
- .byte 58 # DW_AT_decl_file
- .byte 11 # DW_FORM_data1
- .byte 59 # DW_AT_decl_line
- .byte 11 # DW_FORM_data1
- .byte 39 # DW_AT_prototyped
- .byte 25 # DW_FORM_flag_present
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 10 # Abbreviation Code
- .byte 5 # DW_TAG_formal_parameter
- .byte 0 # DW_CHILDREN_no
- .byte 2 # DW_AT_location
- .byte 24 # DW_FORM_exprloc
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 0 # EOM(3)
+
.section .debug_info,"", at progbits
.Lcu_begin0:
- .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+ .long .Ldebug_info_end0-.Ldebug_info_start0
.Ldebug_info_start0:
.short 4 # DWARF version number
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
- .byte 1 # Abbrev [1] 0xb:0xba DW_TAG_compile_unit
+ .byte 1 # Abbrev [1] 0xb:0x30 DW_TAG_compile_unit
.long .Linfo_string0 # DW_AT_producer
.short 29 # DW_AT_language
.long .Linfo_string1 # DW_AT_name
@@ -715,113 +296,32 @@ common_inline_function: # @common_inline_function
.long .Linfo_string2 # DW_AT_comp_dir
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc
- .byte 2 # Abbrev [2] 0x2a:0x11 DW_TAG_variable
- .long 59 # DW_AT_type
- .byte 1 # DW_AT_decl_file
- .byte 7 # DW_AT_decl_line
- .byte 9 # DW_AT_location
- .byte 3
- .quad .L.str
- .byte 3 # Abbrev [3] 0x3b:0xc DW_TAG_array_type
- .long 71 # DW_AT_type
- .byte 4 # Abbrev [4] 0x40:0x6 DW_TAG_subrange_type
- .long 78 # DW_AT_type
- .byte 28 # DW_AT_count
- .byte 0 # End Of Children Mark
- .byte 5 # Abbrev [5] 0x47:0x7 DW_TAG_base_type
- .long .Linfo_string3 # DW_AT_name
- .byte 6 # DW_AT_encoding
- .byte 1 # DW_AT_byte_size
- .byte 6 # Abbrev [6] 0x4e:0x7 DW_TAG_base_type
- .long .Linfo_string4 # DW_AT_name
- .byte 8 # DW_AT_byte_size
- .byte 7 # DW_AT_encoding
- .byte 7 # Abbrev [7] 0x55:0x32 DW_TAG_subprogram
+ .byte 2 # Abbrev [2] 0x2a:0x10 DW_TAG_subprogram
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
- .byte 1 # DW_AT_frame_base
- .byte 86
- .long .Linfo_string5 # DW_AT_name
+ .long .Linfo_string3 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 4 # DW_AT_decl_line
- # DW_AT_external
- .byte 8 # Abbrev [8] 0x6a:0xe DW_TAG_variable
- .byte 2 # DW_AT_location
- .byte 145
- .byte 124
- .long .Linfo_string8 # DW_AT_name
- .byte 1 # DW_AT_decl_file
- .byte 5 # DW_AT_decl_line
- .long 189 # DW_AT_type
- .byte 8 # Abbrev [8] 0x78:0xe DW_TAG_variable
- .byte 2 # DW_AT_location
- .byte 145
- .byte 120
- .long .Linfo_string9 # DW_AT_name
- .byte 1 # DW_AT_decl_file
- .byte 6 # DW_AT_decl_line
- .long 189 # DW_AT_type
- .byte 0 # End Of Children Mark
- .byte 9 # Abbrev [9] 0x87:0x36 DW_TAG_subprogram
+ .byte 2 # Abbrev [2] 0x3a:0x10 DW_TAG_subprogram
.quad .Lfunc_begin1 # DW_AT_low_pc
.long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
- .byte 1 # DW_AT_frame_base
- .byte 86
- .long .Linfo_string6 # DW_AT_name
- .byte 2 # DW_AT_decl_file
- .byte 4 # DW_AT_decl_line
- # DW_AT_prototyped
- .long 189 # DW_AT_type
- .byte 10 # Abbrev [10] 0xa0:0xe DW_TAG_formal_parameter
- .byte 2 # DW_AT_location
- .byte 145
- .byte 124
- .long .Linfo_string10 # DW_AT_name
- .byte 2 # DW_AT_decl_file
- .byte 4 # DW_AT_decl_line
- .long 189 # DW_AT_type
- .byte 8 # Abbrev [8] 0xae:0xe DW_TAG_variable
- .byte 2 # DW_AT_location
- .byte 145
- .byte 120
- .long .Linfo_string9 # DW_AT_name
+ .long .Linfo_string4 # DW_AT_name
.byte 2 # DW_AT_decl_file
.byte 5 # DW_AT_decl_line
- .long 189 # DW_AT_type
- .byte 0 # End Of Children Mark
- .byte 5 # Abbrev [5] 0xbd:0x7 DW_TAG_base_type
- .long .Linfo_string7 # DW_AT_name
- .byte 5 # DW_AT_encoding
- .byte 4 # DW_AT_byte_size
.byte 0 # End Of Children Mark
.Ldebug_info_end0:
+
.section .debug_str,"MS", at progbits,1
.Linfo_string0:
- .asciz "clang version 18.0.0" # string offset=0
+ .asciz "clang version 18.0.0"
.Linfo_string1:
- .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file2.c" # string offset=43
+ .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file2.c"
.Linfo_string2:
- .asciz "/repo/llvm-project" # string offset=125
+ .asciz "/repo/llvm-project"
.Linfo_string3:
- .asciz "char" # string offset=173
+ .asciz "helper_function"
.Linfo_string4:
- .asciz "__ARRAY_SIZE_TYPE__" # string offset=178
-.Linfo_string5:
- .asciz "helper_function" # string offset=198
-.Linfo_string6:
- .asciz "common_inline_function" # string offset=214
-.Linfo_string7:
- .asciz "int" # string offset=237
-.Linfo_string8:
- .asciz "value" # string offset=241
-.Linfo_string9:
- .asciz "result" # string offset=247
-.Linfo_string10:
- .asciz "x" # string offset=254
- .ident "clang version 18.0.0"
- .section ".note.GNU-stack","", at progbits
- .addrsig
- .addrsig_sym common_inline_function
- .addrsig_sym printf
+ .asciz "common_inline_function"
+
.section .debug_line,"", at progbits
.Lline_table_start0:
More information about the llvm-commits
mailing list