diff --git a/bolt/src/BinaryContext.cpp b/bolt/src/BinaryContext.cpp index 246745a1acf3..4bc20b2e369e 100644 --- a/bolt/src/BinaryContext.cpp +++ b/bolt/src/BinaryContext.cpp @@ -1396,6 +1396,16 @@ void BinaryContext::printGlobalSymbols(raw_ostream& OS) const { } } +Expected +BinaryContext::getDwarfFile(StringRef Directory, StringRef FileName, + unsigned FileNumber, + Optional Checksum, + Optional Source, unsigned CUID) { + DwarfLineTable &Table = DwarfLineTablesCUMap[CUID]; + return Table.tryGetFile(Directory, FileName, Checksum, Source, + Ctx->getDwarfVersion(), FileNumber); +} + unsigned BinaryContext::addDebugFilenameToUnit(const uint32_t DestCUID, const uint32_t SrcCUID, unsigned FileIndex) { @@ -1421,7 +1431,7 @@ unsigned BinaryContext::addDebugFilenameToUnit(const uint32_t DestCUID, dwarf::toString(FileNames[FileIndex - 1].Name)) FileName = *FName; assert(FileName != ""); - return cantFail(Ctx->getDwarfFile(Dir, FileName, 0, None, None, DestCUID)); + return cantFail(getDwarfFile(Dir, FileName, 0, None, None, DestCUID)); } std::vector BinaryContext::getSortedFunctions() { @@ -1545,12 +1555,12 @@ void BinaryContext::preprocessDebugInfo() { LineTable->Prologue.FileNames; // Assign a unique label to every line table, one per CU. - Ctx->getMCDwarfLineTable(CUID).setLabel( - Ctx->getOrCreateSymbol(GlobalPrefix + "line_table_start" + Twine(CUID))); + getDwarfLineTable(CUID).setLabel(Ctx->getOrCreateSymbol( + GlobalPrefix + "line_table_start" + Twine(CUID))); // Make sure empty debug line tables are registered too. if (FileNames.empty()) { - cantFail(Ctx->getDwarfFile("", "", 0, None, None, CUID)); + cantFail(getDwarfFile("", "", 0, None, None, CUID)); continue; } for (size_t I = 0, Size = FileNames.size(); I != Size; ++I) { @@ -1566,7 +1576,7 @@ void BinaryContext::preprocessDebugInfo() { if (Optional FName = dwarf::toString(FileNames[I].Name)) FileName = *FName; assert(FileName != ""); - cantFail(Ctx->getDwarfFile(Dir, FileName, 0, None, None, CUID)); + cantFail(getDwarfFile(Dir, FileName, 0, None, None, CUID)); } } diff --git a/bolt/src/BinaryContext.h b/bolt/src/BinaryContext.h index 006d5cd71351..02af430b204c 100644 --- a/bolt/src/BinaryContext.h +++ b/bolt/src/BinaryContext.h @@ -28,6 +28,7 @@ #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" @@ -207,6 +208,9 @@ class BinaryContext { /// Preprocess DWO debug information. void preprocessDWODebugInfo(); + /// DWARF line info for CUs. + std::map DwarfLineTablesCUMap; + public: static std::unique_ptr createBinaryContext(const ObjectFile *File, bool IsPIC, @@ -221,6 +225,19 @@ public: /// Get Number of DWOCUs in a map. uint32_t getNumDWOCUs() { return DWOCUs.size(); } + const std::map &getDwarfLineTables() const { + return DwarfLineTablesCUMap; + } + + DwarfLineTable &getDwarfLineTable(unsigned CUID) { + return DwarfLineTablesCUMap[CUID]; + } + + Expected getDwarfFile(StringRef Directory, StringRef FileName, + unsigned FileNumber, + Optional Checksum, + Optional Source, unsigned CUID); + /// [start memory address] -> [segment info] mapping. std::map SegmentMapInfo; diff --git a/bolt/src/BinaryEmitter.cpp b/bolt/src/BinaryEmitter.cpp index 1736eb4bab80..74ede5533e9f 100644 --- a/bolt/src/BinaryEmitter.cpp +++ b/bolt/src/BinaryEmitter.cpp @@ -210,8 +210,10 @@ void BinaryEmitter::emitAll(StringRef OrgSecPrefix) { emitFunctions(); - if (opts::UpdateDebugSections) + if (opts::UpdateDebugSections) { emitDebugLineInfoForOriginalFunctions(); + DwarfLineTable::emit(BC, Streamer); + } emitDataSections(OrgSecPrefix); @@ -654,7 +656,16 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc, Flags, CurrentRow.Isa, CurrentRow.Discriminator); - BC.Ctx->setDwarfCompileUnitID(FunctionUnitIndex); + const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc(); + BC.Ctx->clearDwarfLocSeen(); + + MCSymbol *LineSym = BC.Ctx->createTempSymbol(); + Streamer.emitLabel(LineSym); + + BC.getDwarfLineTable(FunctionUnitIndex) + .getMCLineSections() + .addLineEntry(MCDwarfLineEntry(LineSym, DwarfLoc), + Streamer.getCurrentSectionOnly()); return NewLoc; } @@ -1010,34 +1021,30 @@ void BinaryEmitter::emitDebugLineInfoForOriginalFunctions() { uint64_t Address = It.first; if (LineTable->lookupAddressRange({Address, 0}, Function.getMaxSize(), Results)) { - MCLineSection &OutputLineTable = - BC.Ctx->getMCDwarfLineTable(Unit->getOffset()).getMCLineSections(); + BinaryLineSection &OutputLineTable = + BC.getDwarfLineTable(Unit->getOffset()).getBinaryLineSections(); for (uint32_t RowIndex : Results) { const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex]; BC.Ctx->setCurrentDwarfLoc( - Row.File, - Row.Line, - Row.Column, + Row.File, Row.Line, Row.Column, (DWARF2_FLAG_IS_STMT * Row.IsStmt) | - (DWARF2_FLAG_BASIC_BLOCK * Row.BasicBlock) | - (DWARF2_FLAG_PROLOGUE_END * Row.PrologueEnd) | - (DWARF2_FLAG_EPILOGUE_BEGIN * Row.EpilogueBegin), - Row.Isa, - Row.Discriminator, - Row.Address.Address); + (DWARF2_FLAG_BASIC_BLOCK * Row.BasicBlock) | + (DWARF2_FLAG_PROLOGUE_END * Row.PrologueEnd) | + (DWARF2_FLAG_EPILOGUE_BEGIN * Row.EpilogueBegin), + Row.Isa, Row.Discriminator); MCDwarfLoc Loc = BC.Ctx->getCurrentDwarfLoc(); BC.Ctx->clearDwarfLocSeen(); - OutputLineTable.addLineEntry(MCDwarfLineEntry{nullptr, Loc}, - FunctionSection); + OutputLineTable.addLineEntry( + BinaryDwarfLineEntry{Row.Address.Address, Loc}, FunctionSection); } // Add an empty entry past the end of the function // for end_sequence mark. - BC.Ctx->setCurrentDwarfLoc(0, 0, 0, 0, 0, 0, - Address + Function.getMaxSize()); + BC.Ctx->setCurrentDwarfLoc(0, 0, 0, 0, 0, 0); MCDwarfLoc Loc = BC.Ctx->getCurrentDwarfLoc(); BC.Ctx->clearDwarfLocSeen(); - OutputLineTable.addLineEntry(MCDwarfLineEntry{nullptr, Loc}, - FunctionSection); + OutputLineTable.addLineEntry( + BinaryDwarfLineEntry{Address + Function.getMaxSize(), Loc}, + FunctionSection); } else { LLVM_DEBUG(dbgs() << "BOLT-DEBUG: function " << Function << " has no associated line number information\n"); diff --git a/bolt/src/DWARFRewriter.cpp b/bolt/src/DWARFRewriter.cpp index fca4ea8d5ce3..7d2277a6641c 100644 --- a/bolt/src/DWARFRewriter.cpp +++ b/bolt/src/DWARFRewriter.cpp @@ -705,7 +705,7 @@ void DWARFRewriter::updateLineTableOffsets() { for (const std::unique_ptr &CU : BC.DwCtx->compile_units()) { const unsigned CUID = CU->getOffset(); - MCSymbol *Label = BC.Ctx->getMCDwarfLineTable(CUID).getLabel(); + MCSymbol *Label = BC.getDwarfLineTable(CUID).getLabel(); if (!Label) continue; diff --git a/bolt/src/DebugData.cpp b/bolt/src/DebugData.cpp index b8a3134b7707..44d7e4b2df1d 100644 --- a/bolt/src/DebugData.cpp +++ b/bolt/src/DebugData.cpp @@ -12,6 +12,7 @@ #include "BinaryBasicBlock.h" #include "BinaryFunction.h" +#include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/EndianStream.h" @@ -522,5 +523,204 @@ std::unique_ptr DebugAbbrevWriter::finalize() { return std::make_unique(ReturnBuffer); } +static void emitDwarfSetLineAddrAbs(MCStreamer &OS, + MCDwarfLineTableParams Params, + int64_t LineDelta, uint64_t Address, + int PointerSize) { + // emit the sequence to set the address + OS.emitIntValue(dwarf::DW_LNS_extended_op, 1); + OS.emitULEB128IntValue(PointerSize + 1); + OS.emitIntValue(dwarf::DW_LNE_set_address, 1); + OS.emitIntValue(Address, PointerSize); + + // emit the sequence for the LineDelta (from 1) and a zero address delta. + MCDwarfLineAddr::Emit(&OS, Params, LineDelta, 0); +} + +static inline void emitBinaryDwarfLineTable( + MCStreamer *MCOS, MCDwarfLineTableParams Params, + const BinaryLineSection::BinaryDwarfLineEntryCollection &LineEntries) { + unsigned FileNum = 1; + unsigned LastLine = 1; + unsigned Column = 0; + unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; + unsigned Isa = 0; + unsigned Discriminator = 0; + uint64_t LastAddress = -1ULL; + const MCAsmInfo *AsmInfo = MCOS->getContext().getAsmInfo(); + + // Loop through each line entry and encode the dwarf line number table. + for (auto It = LineEntries.begin(), Ie = LineEntries.end(); It != Ie; ++It) { + const BinaryDwarfLineEntry &LineEntry = *It; + int64_t LineDelta = static_cast(LineEntry.getLine()) - LastLine; + + const uint64_t Address = LineEntry.getAddress(); + if (std::next(It) == Ie) { + // If emitting absolute addresses, the last entry only carries address + // info for the DW_LNE_end_sequence. This entry compensates for the lack + // of the section context used to emit the end of section label. + MCDwarfLineAddr::Emit(MCOS, Params, INT64_MAX, Address - LastAddress); + return; + } + + if (FileNum != LineEntry.getFileNum()) { + FileNum = LineEntry.getFileNum(); + MCOS->emitInt8(dwarf::DW_LNS_set_file); + MCOS->emitULEB128IntValue(FileNum); + } + if (Column != LineEntry.getColumn()) { + Column = LineEntry.getColumn(); + MCOS->emitInt8(dwarf::DW_LNS_set_column); + MCOS->emitULEB128IntValue(Column); + } + if (Discriminator != LineEntry.getDiscriminator() && + MCOS->getContext().getDwarfVersion() >= 4) { + Discriminator = LineEntry.getDiscriminator(); + unsigned Size = getULEB128Size(Discriminator); + MCOS->emitInt8(dwarf::DW_LNS_extended_op); + MCOS->emitULEB128IntValue(Size + 1); + MCOS->emitInt8(dwarf::DW_LNE_set_discriminator); + MCOS->emitULEB128IntValue(Discriminator); + } + if (Isa != LineEntry.getIsa()) { + Isa = LineEntry.getIsa(); + MCOS->emitInt8(dwarf::DW_LNS_set_isa); + MCOS->emitULEB128IntValue(Isa); + } + if ((LineEntry.getFlags() ^ Flags) & DWARF2_FLAG_IS_STMT) { + Flags = LineEntry.getFlags(); + MCOS->emitInt8(dwarf::DW_LNS_negate_stmt); + } + if (LineEntry.getFlags() & DWARF2_FLAG_BASIC_BLOCK) + MCOS->emitInt8(dwarf::DW_LNS_set_basic_block); + if (LineEntry.getFlags() & DWARF2_FLAG_PROLOGUE_END) + MCOS->emitInt8(dwarf::DW_LNS_set_prologue_end); + if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) + MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin); + + if (LastAddress == -1ULL) { + emitDwarfSetLineAddrAbs(*MCOS, Params, LineDelta, Address, + AsmInfo->getCodePointerSize()); + } else { + MCDwarfLineAddr::Emit(MCOS, Params, LineDelta, Address - LastAddress); + } + LastAddress = Address; + + Discriminator = 0; + LastLine = LineEntry.getLine(); + } +} + +static inline void emitDwarfLineTable( + MCStreamer *MCOS, MCSection *Section, + const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { + unsigned FileNum = 1; + unsigned LastLine = 1; + unsigned Column = 0; + unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; + unsigned Isa = 0; + unsigned Discriminator = 0; + MCSymbol *LastLabel = nullptr; + const MCAsmInfo *AsmInfo = MCOS->getContext().getAsmInfo(); + + // Loop through each MCDwarfLineEntry and encode the dwarf line number table. + for (const MCDwarfLineEntry &LineEntry : LineEntries) { + int64_t LineDelta = static_cast(LineEntry.getLine()) - LastLine; + + if (FileNum != LineEntry.getFileNum()) { + FileNum = LineEntry.getFileNum(); + MCOS->emitInt8(dwarf::DW_LNS_set_file); + MCOS->emitULEB128IntValue(FileNum); + } + if (Column != LineEntry.getColumn()) { + Column = LineEntry.getColumn(); + MCOS->emitInt8(dwarf::DW_LNS_set_column); + MCOS->emitULEB128IntValue(Column); + } + if (Discriminator != LineEntry.getDiscriminator() && + MCOS->getContext().getDwarfVersion() >= 4) { + Discriminator = LineEntry.getDiscriminator(); + unsigned Size = getULEB128Size(Discriminator); + MCOS->emitInt8(dwarf::DW_LNS_extended_op); + MCOS->emitULEB128IntValue(Size + 1); + MCOS->emitInt8(dwarf::DW_LNE_set_discriminator); + MCOS->emitULEB128IntValue(Discriminator); + } + if (Isa != LineEntry.getIsa()) { + Isa = LineEntry.getIsa(); + MCOS->emitInt8(dwarf::DW_LNS_set_isa); + MCOS->emitULEB128IntValue(Isa); + } + if ((LineEntry.getFlags() ^ Flags) & DWARF2_FLAG_IS_STMT) { + Flags = LineEntry.getFlags(); + MCOS->emitInt8(dwarf::DW_LNS_negate_stmt); + } + if (LineEntry.getFlags() & DWARF2_FLAG_BASIC_BLOCK) + MCOS->emitInt8(dwarf::DW_LNS_set_basic_block); + if (LineEntry.getFlags() & DWARF2_FLAG_PROLOGUE_END) + MCOS->emitInt8(dwarf::DW_LNS_set_prologue_end); + if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) + MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin); + + MCSymbol *Label = LineEntry.getLabel(); + + // At this point we want to emit/create the sequence to encode the delta + // in line numbers and the increment of the address from the previous + // Label and the current Label. + MCOS->emitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label, + AsmInfo->getCodePointerSize()); + Discriminator = 0; + LastLine = LineEntry.getLine(); + LastLabel = Label; + } + + // Generate DWARF line end entry. + MCOS->emitDwarfLineEndEntry(Section, LastLabel); +} + +void DwarfLineTable::emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params, + Optional &LineStr) const { + MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second; + + // Put out the line tables. + for (const auto &LineSec : MCLineSections.getMCLineEntries()) + emitDwarfLineTable(MCOS, LineSec.first, LineSec.second); + + // Emit line tables for the original code. + for (const auto &LineSec : BinaryLineSections.getBinaryLineEntries()) + emitBinaryDwarfLineTable(MCOS, Params, LineSec.second); + + // This is the end of the section, so set the value of the symbol at the end + // of this section (that was used in a previous expression). + MCOS->emitLabel(LineEndSym); +} + +void DwarfLineTable::emit(BinaryContext &BC, MCStreamer &Streamer) { + MCAssembler &Assembler = + static_cast(&Streamer)->getAssembler(); + + MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams(); + + auto &LineTables = BC.getDwarfLineTables(); + + // Bail out early so we don't switch to the debug_line section needlessly and + // in doing so create an unnecessary (if empty) section. + if (LineTables.empty()) + return; + + // In a v5 non-split line table, put the strings in a separate section. + Optional LineStr(None); + if (BC.Ctx->getDwarfVersion() >= 5) + LineStr = MCDwarfLineStr(*BC.Ctx); + + // Switch to the section where the table will be emitted into. + Streamer.SwitchSection(BC.MOFI->getDwarfLineSection()); + + // Handle the rest of the Compile Units. + for (auto &CUIDTablePair : LineTables) { + CUIDTablePair.second.emitCU(&Streamer, Params, LineStr); + } +} + } // namespace bolt } // namespace llvm diff --git a/bolt/src/DebugData.h b/bolt/src/DebugData.h index 5b45e3dc3874..a121ee1eae81 100644 --- a/bolt/src/DebugData.h +++ b/bolt/src/DebugData.h @@ -15,6 +15,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/MC/MCDwarf.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/raw_ostream.h" #include @@ -497,6 +498,76 @@ public: } }; +/// Similar to MCDwarfLineEntry, but identifies the location by its address +/// instead of MCLabel. +class BinaryDwarfLineEntry : public MCDwarfLoc { + uint64_t Address; + +public: + // Constructor to create an BinaryDwarfLineEntry given a symbol and the dwarf + // loc. + BinaryDwarfLineEntry(uint64_t Address, const MCDwarfLoc loc) + : MCDwarfLoc(loc), Address(Address) {} + + uint64_t getAddress() const { return Address; } +}; + +/// Similar to MCLineSection, store line entries per CU but use absolute +/// addresses for line locations. +class BinaryLineSection { +public: + // Add an entry to this MCLineSection's line entries. + void addLineEntry(const BinaryDwarfLineEntry &LineEntry, MCSection *Sec) { + BinaryLineDivisions[Sec].push_back(LineEntry); + } + + using BinaryDwarfLineEntryCollection = std::vector; + using BinaryLineDivisionMap = + MapVector; + +private: + // A collection of BinaryDwarfLineEntry for each section. + BinaryLineDivisionMap BinaryLineDivisions; + +public: + // Returns the collection of BinaryDwarfLineEntry for a given Compile Unit ID. + const BinaryLineDivisionMap &getBinaryLineEntries() const { + return BinaryLineDivisions; + } +}; + +/// Line number information for all parts of the binary. +class DwarfLineTable { + MCDwarfLineTableHeader Header; + MCLineSection MCLineSections; + BinaryLineSection BinaryLineSections; + +public: + /// Emit line info for all units in the binary context. + static void emit(BinaryContext &BC, MCStreamer &Streamer); + + // Emit the Dwarf file and the line tables for a given CU. + void emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params, + Optional &LineStr) const; + + Expected tryGetFile(StringRef &Directory, StringRef &FileName, + Optional Checksum, + Optional Source, + uint16_t DwarfVersion, + unsigned FileNumber = 0) { + return Header.tryGetFile(Directory, FileName, Checksum, Source, + DwarfVersion, FileNumber); + } + + MCSymbol *getLabel() const { return Header.Label; } + + void setLabel(MCSymbol *Label) { Header.Label = Label; } + + MCLineSection &getMCLineSections() { return MCLineSections; } + + BinaryLineSection &getBinaryLineSections() { return BinaryLineSections; } +}; + } // namespace bolt } // namespace llvm