diff --git a/include/llvm/DebugInfo/CodeView/Line.h b/include/llvm/DebugInfo/CodeView/Line.h index 0e7057fb540..5defdf1b2de 100644 --- a/include/llvm/DebugInfo/CodeView/Line.h +++ b/include/llvm/DebugInfo/CodeView/Line.h @@ -21,17 +21,26 @@ using llvm::support::ulittle32_t; class LineInfo { public: - static const uint32_t AlwaysStepIntoLineNumber = 0xfeefee; - static const uint32_t NeverStepIntoLineNumber = 0xf00f00; + enum : uint32_t { + AlwaysStepIntoLineNumber = 0xfeefee, + NeverStepIntoLineNumber = 0xf00f00 + }; -private: - static const uint32_t StartLineMask = 0x00ffffff; - static const uint32_t EndLineDeltaMask = 0x7f000000; - static const int EndLineDeltaShift = 24; - static const uint32_t StatementFlag = 0x80000000u; + enum : int { EndLineDeltaShift = 24 }; -public: - LineInfo(uint32_t StartLine, uint32_t EndLine, bool IsStatement); + enum : uint32_t { + StartLineMask = 0x00ffffff, + EndLineDeltaMask = 0x7f000000, + StatementFlag = 0x80000000u + }; + + LineInfo(uint32_t StartLine, uint32_t EndLine, bool IsStatement) { + LineData = StartLine & StartLineMask; + uint32_t Delta = EndLine - StartLine; + LineData |= (Delta << EndLineDeltaShift) & EndLineDeltaShift; + if (IsStatement) + LineData |= StatementFlag; + } uint32_t getStartLine() const { return LineData & StartLineMask; } @@ -151,6 +160,10 @@ struct FileChecksum { // Checksum bytes follow. }; +enum LineFlags : uint32_t { + HaveColumns = 1, // CV_LINES_HAVE_COLUMNS +}; + } // namespace codeview } // namespace llvm diff --git a/include/llvm/MC/MCCodeView.h b/include/llvm/MC/MCCodeView.h new file mode 100644 index 00000000000..ed2f35a8f94 --- /dev/null +++ b/include/llvm/MC/MCCodeView.h @@ -0,0 +1,162 @@ +//===- MCCodeView.h - Machine Code CodeView support -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Holds state from .cv_file and .cv_loc directives for later emission. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCCODEVIEW_H +#define LLVM_MC_MCCODEVIEW_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCFragment.h" +#include +#include + +namespace llvm { +class MCContext; +class MCObjectStreamer; +class MCStreamer; + +/// \brief Instances of this class represent the information from a +/// .cv_loc directive. +class MCCVLoc { + uint32_t FunctionId; + uint32_t FileNum; + uint32_t Line; + uint16_t Column; + uint16_t PrologueEnd : 1; + uint16_t IsStmt : 1; + +private: // MCContext manages these + friend class MCContext; + MCCVLoc(unsigned functionid, unsigned fileNum, unsigned line, unsigned column, + bool prologueend, bool isstmt) + : FunctionId(functionid), FileNum(fileNum), Line(line), Column(column), + PrologueEnd(prologueend), IsStmt(isstmt) {} + + // Allow the default copy constructor and assignment operator to be used + // for an MCCVLoc object. + +public: + unsigned getFunctionId() const { return FunctionId; } + + /// \brief Get the FileNum of this MCCVLoc. + unsigned getFileNum() const { return FileNum; } + + /// \brief Get the Line of this MCCVLoc. + unsigned getLine() const { return Line; } + + /// \brief Get the Column of this MCCVLoc. + unsigned getColumn() const { return Column; } + + bool isPrologueEnd() const { return PrologueEnd; } + bool isStmt() const { return IsStmt; } + + void setFunctionId(unsigned FID) { FunctionId = FID; } + + /// \brief Set the FileNum of this MCCVLoc. + void setFileNum(unsigned fileNum) { FileNum = fileNum; } + + /// \brief Set the Line of this MCCVLoc. + void setLine(unsigned line) { Line = line; } + + /// \brief Set the Column of this MCCVLoc. + void setColumn(unsigned column) { + assert(column <= UINT16_MAX); + Column = column; + } + + void setPrologueEnd(bool PE) { PrologueEnd = PE; } + void setIsStmt(bool IS) { IsStmt = IS; } +}; + +/// \brief Instances of this class represent the line information for +/// the CodeView line table entries. Which is created after a machine +/// instruction is assembled and uses an address from a temporary label +/// created at the current address in the current section and the info from +/// the last .cv_loc directive seen as stored in the context. +class MCCVLineEntry : public MCCVLoc { + MCSymbol *Label; + +private: + // Allow the default copy constructor and assignment operator to be used + // for an MCCVLineEntry object. + +public: + // Constructor to create an MCCVLineEntry given a symbol and the dwarf loc. + MCCVLineEntry(MCSymbol *label, const MCCVLoc loc) + : MCCVLoc(loc), Label(label) {} + + MCSymbol *getLabel() const { return Label; } + + // This is called when an instruction is assembled into the specified + // section and if there is information from the last .cv_loc directive that + // has yet to have a line entry made for it is made. + static void Make(MCObjectStreamer *MCOS); +}; + +/// Holds state from .cv_file and .cv_loc directives for later emission. +class CodeViewContext { +public: + CodeViewContext(); + ~CodeViewContext(); + + bool isValidFileNumber(unsigned FileNumber) const; + bool addFile(unsigned FileNumber, StringRef Filename); + ArrayRef getFilenames() { return Filenames; } + + /// \brief Add a line entry. + void addLineEntry(const MCCVLineEntry &LineEntry) { + MCCVLines[LineEntry.getFunctionId()].push_back(LineEntry); + } + + ArrayRef getFunctionLineEntries(unsigned FuncId) { + assert(MCCVLines.find(FuncId) != MCCVLines.end()); + return MCCVLines.find(FuncId)->second; + } + + /// Emits a line table substream. + void emitLineTableForFunction(MCObjectStreamer &OS, unsigned FuncId, + const MCSymbol *FuncBegin, + const MCSymbol *FuncEnd); + + /// Emits the string table substream. + void emitStringTable(MCObjectStreamer &OS); + + /// Emits the file checksum substream. + void emitFileChecksums(MCObjectStreamer &OS); + +private: + /// Map from string to string table offset. + StringMap StringTable; + + /// The fragment that ultimately holds our strings. + MCDataFragment *StrTabFragment = nullptr; + bool InsertedStrTabFragment = false; + + MCDataFragment *getStringTableFragment(); + + /// Add something to the string table. + StringRef addToStringTable(StringRef S); + + /// Get a string table offset. + unsigned getStringTableOffset(StringRef S); + + /// An array of absolute paths. Eventually this may include the file checksum. + SmallVector Filenames; + + /// A collection of MCDwarfLineEntry for each section. + std::map> MCCVLines; +}; + +} // end namespace llvm +#endif diff --git a/include/llvm/MC/MCContext.h b/include/llvm/MC/MCContext.h index e5a9afd9968..608d15a27cf 100644 --- a/include/llvm/MC/MCContext.h +++ b/include/llvm/MC/MCContext.h @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/Twine.h" +#include "llvm/MC/MCCodeView.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/SectionKind.h" @@ -42,6 +43,7 @@ namespace llvm { class MCSectionMachO; class MCSectionELF; class MCSectionCOFF; + class CodeViewContext; /// Context object for machine code objects. This class owns all of the /// sections that it creates. @@ -66,6 +68,8 @@ namespace llvm { /// The MCObjectFileInfo for this target. const MCObjectFileInfo *MOFI; + std::unique_ptr CVContext; + /// Allocator object used for creating machine code objects. /// /// We use a bump pointer allocator to avoid the need to track all allocated @@ -135,6 +139,10 @@ namespace llvm { MCDwarfLoc CurrentDwarfLoc; bool DwarfLocSeen; + /// The current CodeView line information from the last .cv_loc directive. + MCCVLoc CurrentCVLoc = MCCVLoc(0, 0, 0, 0, false, true); + bool CVLocSeen = false; + /// Generate dwarf debugging info for assembly source files. bool GenDwarfForAssembly; @@ -237,6 +245,8 @@ namespace llvm { const MCObjectFileInfo *getObjectFileInfo() const { return MOFI; } + CodeViewContext &getCVContext(); + void setAllowTemporaryLabels(bool Value) { AllowTemporaryLabels = Value; } void setUseNamesOnTempLabels(bool Value) { UseNamesOnTempLabels = Value; } @@ -505,6 +515,35 @@ namespace llvm { /// @} + + /// \name CodeView Management + /// @{ + + /// Creates an entry in the cv file table. + unsigned getCVFile(StringRef FileName, unsigned FileNumber); + + /// Saves the information from the currently parsed .cv_loc directive + /// and sets CVLocSeen. When the next instruction is assembled an entry + /// in the line number table with this information and the address of the + /// instruction will be created. + void setCurrentCVLoc(unsigned FunctionId, unsigned FileNo, unsigned Line, + unsigned Column, bool PrologueEnd, bool IsStmt) { + CurrentCVLoc.setFunctionId(FunctionId); + CurrentCVLoc.setFileNum(FileNo); + CurrentCVLoc.setLine(Line); + CurrentCVLoc.setColumn(Column); + CurrentCVLoc.setPrologueEnd(PrologueEnd); + CurrentCVLoc.setIsStmt(IsStmt); + CVLocSeen = true; + } + void clearCVLocSeen() { CVLocSeen = false; } + + bool getCVLocSeen() { return CVLocSeen; } + const MCCVLoc &getCurrentCVLoc() { return CurrentCVLoc; } + + bool isValidCVFileNumber(unsigned FileNumber); + /// @} + char *getSecureLogFile() { return SecureLogFile; } raw_fd_ostream *getSecureLog() { return SecureLog.get(); } bool getSecureLogUsed() { return SecureLogUsed; } diff --git a/include/llvm/MC/MCObjectStreamer.h b/include/llvm/MC/MCObjectStreamer.h index 9fe2fda2135..fe5575c5018 100644 --- a/include/llvm/MC/MCObjectStreamer.h +++ b/include/llvm/MC/MCObjectStreamer.h @@ -59,7 +59,6 @@ public: void EmitFrames(MCAsmBackend *MAB); void EmitCFISections(bool EH, bool Debug) override; -protected: MCFragment *getCurrentFragment() const; void insert(MCFragment *F) { @@ -73,6 +72,7 @@ protected: /// fragment is not a data fragment. MCDataFragment *getOrCreateDataFragment(); +protected: bool changeSectionImpl(MCSection *Section, const MCExpr *Subsection); /// If any labels have been emitted but not assigned fragments, ensure that @@ -122,6 +122,13 @@ public: unsigned PointerSize); void EmitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, const MCSymbol *Label); + void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, + unsigned Column, bool PrologueEnd, bool IsStmt, + StringRef FileName) override; + void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, + const MCSymbol *End) override; + void EmitCVStringTableDirective() override; + void EmitCVFileChecksumsDirective() override; void EmitGPRel32Value(const MCExpr *Value) override; void EmitGPRel64Value(const MCExpr *Value) override; bool EmitRelocDirective(const MCExpr &Offset, StringRef Name, diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index 04d143ffef6..1f4b24644ca 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -640,6 +640,27 @@ public: unsigned Isa, unsigned Discriminator, StringRef FileName); + /// \brief Associate a filename with a specified logical file number. This + /// implements the '.cv_file 4 "foo.c"' assembler directive. + virtual unsigned EmitCVFileDirective(unsigned FileNo, StringRef Filename); + + /// \brief This implements the CodeView '.cv_loc' assembler directive. + virtual void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, + unsigned Line, unsigned Column, + bool PrologueEnd, bool IsStmt, + StringRef FileName); + + /// \brief This implements the CodeView '.cv_linetable' assembler directive. + virtual void EmitCVLinetableDirective(unsigned FunctionId, + const MCSymbol *FnStart, + const MCSymbol *FnEnd); + + /// \brief This implements the CodeView '.cv_stringtable' assembler directive. + virtual void EmitCVStringTableDirective() {} + + /// \brief This implements the CodeView '.cv_filechecksums' assembler directive. + virtual void EmitCVFileChecksumsDirective() {} + /// Emit the absolute difference between two symbols. /// /// \pre Offset of \c Hi is greater than the offset \c Lo. diff --git a/include/llvm/MC/StringTableBuilder.h b/include/llvm/MC/StringTableBuilder.h index adde86b4558..bf3cea7f03f 100644 --- a/include/llvm/MC/StringTableBuilder.h +++ b/include/llvm/MC/StringTableBuilder.h @@ -27,6 +27,8 @@ private: size_t Size = 0; Kind K; + void finalizeStringTable(bool Optimize); + public: StringTableBuilder(Kind K); @@ -39,6 +41,10 @@ public: /// be added after this point. void finalize(); + /// Finalize the string table without reording it. In this mode, offsets + /// returned by add will still be valid. + void finalizeInOrder(); + /// \brief Retrieve the string table data. Can only be used after the table /// is finalized. StringRef data() const { diff --git a/include/llvm/Support/COFF.h b/include/llvm/Support/COFF.h index bbd851ecfb5..32bedbb2904 100644 --- a/include/llvm/Support/COFF.h +++ b/include/llvm/Support/COFF.h @@ -656,17 +656,7 @@ namespace COFF { } }; - enum CodeViewLine : unsigned { - CVL_LineNumberStartBits = 24, - CVL_LineNumberEndDeltaBits = 7, - CVL_LineNumberEndDeltaMask = (1U << CVL_LineNumberEndDeltaBits) - 1, - CVL_MaxLineNumber = (1U << CVL_LineNumberStartBits) - 1, - CVL_IsStatement = 1U << 31, - CVL_MaxColumnNumber = UINT16_MAX, - }; - enum CodeViewIdentifiers { - DEBUG_LINE_TABLES_HAVE_COLUMN_RECORDS = 0x1, DEBUG_SECTION_MAGIC = 0x4, }; diff --git a/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index f1716361a1d..b503f1dc31e 100644 --- a/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -13,6 +13,7 @@ #include "CodeViewDebug.h" #include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCSymbol.h" @@ -74,6 +75,18 @@ StringRef CodeViewDebug::getFullFilepath(const DIFile *File) { return Filepath; } +unsigned CodeViewDebug::maybeRecordFile(const DIFile *F) { + unsigned NextId = FileIdMap.size() + 1; + auto Insertion = FileIdMap.insert(std::make_pair(F, NextId)); + if (Insertion.second) { + // We have to compute the full filepath and emit a .cv_file directive. + StringRef FullPath = getFullFilepath(F); + NextId = Asm->OutStreamer->EmitCVFileDirective(NextId, FullPath); + assert(NextId == FileIdMap.size() && ".cv_file directive failed"); + } + return Insertion.first->second; +} + void CodeViewDebug::maybeRecordLocation(DebugLoc DL, const MachineFunction *MF) { // Skip this instruction if it has the same location as the previous one. @@ -85,15 +98,26 @@ void CodeViewDebug::maybeRecordLocation(DebugLoc DL, return; // Skip this line if it is longer than the maximum we can record. - if (DL.getLine() > COFF::CVL_MaxLineNumber) + LineInfo LI(DL.getLine(), DL.getLine(), /*IsStatement=*/true); + if (LI.getStartLine() != DL.getLine() || LI.isAlwaysStepInto() || + LI.isNeverStepInto()) return; - CurFn->LastLoc = DL; + ColumnInfo CI(DL.getCol(), /*EndColumn=*/0); + if (CI.getStartColumn() != DL.getCol()) + return; - MCSymbol *MCL = Asm->MMI->getContext().createTempSymbol(); - Asm->OutStreamer->EmitLabel(MCL); - CurFn->Instrs.push_back(MCL); - LabelsAndLocs[MCL] = DL; + if (!CurFn->HaveLineInfo) + CurFn->HaveLineInfo = true; + unsigned FileId = 0; + if (CurFn->LastLoc.get() && CurFn->LastLoc->getFile() == DL->getFile()) + FileId = CurFn->LastFileId; + else + FileId = CurFn->LastFileId = maybeRecordFile(DL->getFile()); + CurFn->LastLoc = DL; + Asm->OutStreamer->EmitCVLocDirective(CurFn->FuncId, FileId, DL.getLine(), + DL.getCol(), /*PrologueEnd=*/false, + /*IsStmt=*/false, DL->getFilename()); } CodeViewDebug::CodeViewDebug(AsmPrinter *AP) @@ -128,39 +152,17 @@ void CodeViewDebug::endModule() { // of the payload followed by the payload itself. The subsections are 4-byte // aligned. - // Emit per-function debug information. This code is extracted into a - // separate function for readability. - for (size_t I = 0, E = VisitedFunctions.size(); I != E; ++I) - emitDebugInfoForFunction(VisitedFunctions[I]); + // Emit per-function debug information. + for (auto &P : FnDebugInfo) + emitDebugInfoForFunction(P.first, P.second); // This subsection holds a file index to offset in string table table. Asm->OutStreamer->AddComment("File index to string table offset subsection"); - Asm->EmitInt32(unsigned(ModuleSubstreamKind::FileChecksums)); - size_t NumFilenames = FileNameRegistry.Infos.size(); - Asm->EmitInt32(8 * NumFilenames); - for (size_t I = 0, E = FileNameRegistry.Filenames.size(); I != E; ++I) { - StringRef Filename = FileNameRegistry.Filenames[I]; - // For each unique filename, just write its offset in the string table. - Asm->EmitInt32(FileNameRegistry.Infos[Filename].StartOffset); - // The function name offset is not followed by any additional data. - Asm->EmitInt32(0); - } + Asm->OutStreamer->EmitCVFileChecksumsDirective(); // This subsection holds the string table. Asm->OutStreamer->AddComment("String table"); - Asm->EmitInt32(unsigned(ModuleSubstreamKind::StringTable)); - Asm->EmitInt32(FileNameRegistry.LastOffset); - // The payload starts with a null character. - Asm->EmitInt8(0); - - for (size_t I = 0, E = FileNameRegistry.Filenames.size(); I != E; ++I) { - // Just emit unique filenames one by one, separated by a null character. - Asm->OutStreamer->EmitBytes(FileNameRegistry.Filenames[I]); - Asm->EmitInt8(0); - } - - // No more subsections. Fill with zeros to align the end of the section by 4. - Asm->OutStreamer->EmitFill((-FileNameRegistry.LastOffset) % 4, 0); + Asm->OutStreamer->EmitCVStringTableDirective(); clear(); } @@ -177,21 +179,13 @@ static void EmitLabelDiff(MCStreamer &Streamer, Streamer.EmitValue(AddrDelta, Size); } -static const DIFile *getFileFromLoc(DebugLoc DL) { - return DL.get()->getScope()->getFile(); -} - -void CodeViewDebug::emitDebugInfoForFunction(const Function *GV) { +void CodeViewDebug::emitDebugInfoForFunction(const Function *GV, + FunctionInfo &FI) { // For each function there is a separate subsection // which holds the PC to file:line table. const MCSymbol *Fn = Asm->getSymbol(GV); assert(Fn); - const FunctionInfo &FI = FnDebugInfo[GV]; - if (FI.Instrs.empty()) - return; - assert(FI.End && "Don't know where the function ends?"); - StringRef FuncName; if (auto *SP = getDISubprogram(GV)) FuncName = SP->getDisplayName(); @@ -238,102 +232,8 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV) { // Every subsection must be aligned to a 4-byte boundary. Asm->OutStreamer->EmitFill((-FuncName.size()) % 4, 0); - // PCs/Instructions are grouped into segments sharing the same filename. - // Pre-calculate the lengths (in instructions) of these segments and store - // them in a map for convenience. Each index in the map is the sequential - // number of the respective instruction that starts a new segment. - DenseMap FilenameSegmentLengths; - size_t LastSegmentEnd = 0; - const DIFile *PrevFile = getFileFromLoc(LabelsAndLocs[FI.Instrs[0]]); - for (size_t J = 1, F = FI.Instrs.size(); J != F; ++J) { - const DIFile *CurFile = getFileFromLoc(LabelsAndLocs[FI.Instrs[J]]); - if (PrevFile == CurFile) - continue; - FilenameSegmentLengths[LastSegmentEnd] = J - LastSegmentEnd; - LastSegmentEnd = J; - PrevFile = CurFile; - } - FilenameSegmentLengths[LastSegmentEnd] = FI.Instrs.size() - LastSegmentEnd; - - // Emit a line table subsection, required to do PC-to-file:line lookup. - Asm->OutStreamer->AddComment("Line table subsection for " + Twine(FuncName)); - Asm->EmitInt32(unsigned(ModuleSubstreamKind::Lines)); - MCSymbol *LineTableBegin = Asm->MMI->getContext().createTempSymbol(), - *LineTableEnd = Asm->MMI->getContext().createTempSymbol(); - EmitLabelDiff(*Asm->OutStreamer, LineTableBegin, LineTableEnd); - Asm->OutStreamer->EmitLabel(LineTableBegin); - - // Identify the function this subsection is for. - Asm->OutStreamer->EmitCOFFSecRel32(Fn); - Asm->OutStreamer->EmitCOFFSectionIndex(Fn); - // Insert flags after a 16-bit section index. - Asm->EmitInt16(COFF::DEBUG_LINE_TABLES_HAVE_COLUMN_RECORDS); - - // Length of the function's code, in bytes. - EmitLabelDiff(*Asm->OutStreamer, Fn, FI.End); - - // PC-to-linenumber lookup table: - MCSymbol *FileSegmentEnd = nullptr; - - // The start of the last segment: - size_t LastSegmentStart = 0; - - auto FinishPreviousChunk = [&] { - if (!FileSegmentEnd) - return; - for (size_t ColSegI = LastSegmentStart, - ColSegEnd = ColSegI + FilenameSegmentLengths[LastSegmentStart]; - ColSegI != ColSegEnd; ++ColSegI) { - unsigned ColumnNumber = LabelsAndLocs[FI.Instrs[ColSegI]].getCol(); - // Truncate the column number if it is longer than the maximum we can - // record. - if (ColumnNumber > COFF::CVL_MaxColumnNumber) - ColumnNumber = 0; - Asm->EmitInt16(ColumnNumber); // Start column - Asm->EmitInt16(0); // End column - } - Asm->OutStreamer->EmitLabel(FileSegmentEnd); - }; - - for (size_t J = 0, F = FI.Instrs.size(); J != F; ++J) { - MCSymbol *Instr = FI.Instrs[J]; - assert(LabelsAndLocs.count(Instr)); - - if (FilenameSegmentLengths.count(J)) { - // We came to a beginning of a new filename segment. - FinishPreviousChunk(); - const DIFile *File = getFileFromLoc(LabelsAndLocs[FI.Instrs[J]]); - StringRef CurFilename = getFullFilepath(File); - size_t IndexInFileTable = FileNameRegistry.add(CurFilename); - // Each segment starts with the offset of the filename - // in the string table. - Asm->OutStreamer->AddComment( - "Segment for file '" + Twine(CurFilename) + "' begins"); - MCSymbol *FileSegmentBegin = Asm->MMI->getContext().createTempSymbol(); - Asm->OutStreamer->EmitLabel(FileSegmentBegin); - Asm->EmitInt32(8 * IndexInFileTable); - - // Number of PC records in the lookup table. - size_t SegmentLength = FilenameSegmentLengths[J]; - Asm->EmitInt32(SegmentLength); - - // Full size of the segment for this filename, including the prev two - // records. - FileSegmentEnd = Asm->MMI->getContext().createTempSymbol(); - EmitLabelDiff(*Asm->OutStreamer, FileSegmentBegin, FileSegmentEnd); - LastSegmentStart = J; - } - - // The first PC with the given linenumber and the linenumber itself. - EmitLabelDiff(*Asm->OutStreamer, Fn, Instr); - uint32_t LineNumber = LabelsAndLocs[Instr].getLine(); - assert(LineNumber <= COFF::CVL_MaxLineNumber); - uint32_t LineData = LineNumber | COFF::CVL_IsStatement; - Asm->EmitInt32(LineData); - } - - FinishPreviousChunk(); - Asm->OutStreamer->EmitLabel(LineTableEnd); + // We have an assembler directive that takes care of the whole line table. + Asm->OutStreamer->EmitCVLinetableDirective(FI.FuncId, Fn, FI.End); } void CodeViewDebug::beginFunction(const MachineFunction *MF) { @@ -344,8 +244,8 @@ void CodeViewDebug::beginFunction(const MachineFunction *MF) { const Function *GV = MF->getFunction(); assert(FnDebugInfo.count(GV) == false); - VisitedFunctions.push_back(GV); CurFn = &FnDebugInfo[GV]; + CurFn->FuncId = NextFuncId++; // Find the end of the function prolog. // FIXME: is there a simpler a way to do this? Can we just search @@ -384,9 +284,9 @@ void CodeViewDebug::endFunction(const MachineFunction *MF) { assert(FnDebugInfo.count(GV)); assert(CurFn == &FnDebugInfo[GV]); - if (CurFn->Instrs.empty()) { + // Don't emit anything if we don't have any line tables. + if (!CurFn->HaveLineInfo) { FnDebugInfo.erase(GV); - VisitedFunctions.pop_back(); } else { CurFn->End = Asm->getFunctionEnd(); } diff --git a/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/lib/CodeGen/AsmPrinter/CodeViewDebug.h index 4294b4fd694..77b5ed39c1f 100644 --- a/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -37,72 +37,38 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public AsmPrinterHandler { // to the end of the function. struct FunctionInfo { DebugLoc LastLoc; - SmallVector Instrs; - MCSymbol *End; - FunctionInfo() : End(nullptr) {} + MCSymbol *End = nullptr; + unsigned FuncId = 0; + unsigned LastFileId; + bool HaveLineInfo = false; }; FunctionInfo *CurFn; - typedef DenseMap FnDebugInfoTy; - FnDebugInfoTy FnDebugInfo; - // Store the functions we've visited in a vector so we can maintain a stable - // order while emitting subsections. - SmallVector VisitedFunctions; + unsigned NextFuncId = 0; - DenseMap LabelsAndLocs; + /// Remember some debug info about each function. Keep it in a stable order to + /// emit at the end of the TU. + MapVector FnDebugInfo; - // FileNameRegistry - Manages filenames observed while generating debug info - // by filtering out duplicates and bookkeeping the offsets in the string - // table to be generated. - struct FileNameRegistryTy { - SmallVector Filenames; - struct PerFileInfo { - size_t FilenameID, StartOffset; - }; - StringMap Infos; - - // The offset in the string table where we'll write the next unique - // filename. - size_t LastOffset; - - FileNameRegistryTy() { - clear(); - } - - // Add Filename to the registry, if it was not observed before. - size_t add(StringRef Filename) { - size_t OldSize = Infos.size(); - bool Inserted; - StringMap::iterator It; - std::tie(It, Inserted) = Infos.insert( - std::make_pair(Filename, PerFileInfo{OldSize, LastOffset})); - if (Inserted) { - LastOffset += Filename.size() + 1; - Filenames.push_back(Filename); - } - return It->second.FilenameID; - } - - void clear() { - LastOffset = 1; - Infos.clear(); - Filenames.clear(); - } - } FileNameRegistry; + /// Map from DIFile to .cv_file id. + DenseMap FileIdMap; typedef std::map FileToFilepathMapTy; FileToFilepathMapTy FileToFilepathMap; StringRef getFullFilepath(const DIFile *S); + unsigned maybeRecordFile(const DIFile *F); + void maybeRecordLocation(DebugLoc DL, const MachineFunction *MF); void clear() { assert(CurFn == nullptr); - FileNameRegistry.clear(); - LabelsAndLocs.clear(); + FileIdMap.clear(); + FnDebugInfo.clear(); + FileToFilepathMap.clear(); } - void emitDebugInfoForFunction(const Function *GV); + void emitDebugInfoForFunction(const Function *GV, FunctionInfo &FI); public: CodeViewDebug(AsmPrinter *Asm); diff --git a/lib/MC/CMakeLists.txt b/lib/MC/CMakeLists.txt index d1aac8104a3..a04f16c6ea7 100644 --- a/lib/MC/CMakeLists.txt +++ b/lib/MC/CMakeLists.txt @@ -10,6 +10,7 @@ add_llvm_library(LLVMMC MCAssembler.cpp MCCodeEmitter.cpp MCCodeGenInfo.cpp + MCCodeView.cpp MCContext.cpp MCDwarf.cpp MCELFObjectTargetWriter.cpp diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index 2de36f8561c..d9382c5df9f 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -199,6 +199,15 @@ public: StringRef FileName) override; MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override; + unsigned EmitCVFileDirective(unsigned FileNo, StringRef Filename) override; + void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, + unsigned Column, bool PrologueEnd, bool IsStmt, + StringRef FileName) override; + void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, + const MCSymbol *FnEnd) override; + void EmitCVStringTableDirective() override; + void EmitCVFileChecksumsDirective() override; + void EmitIdent(StringRef IdentString) override; void EmitCFISections(bool EH, bool Debug) override; void EmitCFIDefCfa(int64_t Register, int64_t Offset) override; @@ -954,6 +963,69 @@ MCSymbol *MCAsmStreamer::getDwarfLineTableSymbol(unsigned CUID) { return MCStreamer::getDwarfLineTableSymbol(0); } +unsigned MCAsmStreamer::EmitCVFileDirective(unsigned FileNo, + StringRef Filename) { + if (!getContext().getCVFile(Filename, FileNo)) + return 0; + + OS << "\t.cv_file\t" << FileNo << ' '; + + PrintQuotedString(Filename, OS); + EmitEOL(); + + return FileNo; +} + +void MCAsmStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, + unsigned Line, unsigned Column, + bool PrologueEnd, bool IsStmt, + StringRef FileName) { + OS << "\t.cv_loc\t" << FunctionId << " " << FileNo << " " << Line << " " + << Column; + if (PrologueEnd) + OS << " prologue_end"; + + unsigned OldIsStmt = getContext().getCurrentCVLoc().isStmt(); + if (IsStmt != OldIsStmt) { + OS << " is_stmt "; + + if (IsStmt) + OS << "1"; + else + OS << "0"; + } + + if (IsVerboseAsm) { + OS.PadToColumn(MAI->getCommentColumn()); + OS << MAI->getCommentString() << ' ' << FileName << ':' + << Line << ':' << Column; + } + EmitEOL(); + this->MCStreamer::EmitCVLocDirective(FunctionId, FileNo, Line, Column, + PrologueEnd, IsStmt, FileName); +} + +void MCAsmStreamer::EmitCVLinetableDirective(unsigned FunctionId, + const MCSymbol *FnStart, + const MCSymbol *FnEnd) { + OS << "\t.cv_linetable\t" << FunctionId << ", "; + FnStart->print(OS, MAI); + OS << ", "; + FnEnd->print(OS, MAI); + EmitEOL(); + this->MCStreamer::EmitCVLinetableDirective(FunctionId, FnStart, FnEnd); +} + +void MCAsmStreamer::EmitCVStringTableDirective() { + OS << "\t.cv_stringtable"; + EmitEOL(); +} + +void MCAsmStreamer::EmitCVFileChecksumsDirective() { + OS << "\t.cv_filechecksums"; + EmitEOL(); +} + void MCAsmStreamer::EmitIdent(StringRef IdentString) { assert(MAI->hasIdentDirective() && ".ident directive not supported"); OS << "\t.ident\t"; diff --git a/lib/MC/MCCodeView.cpp b/lib/MC/MCCodeView.cpp new file mode 100644 index 00000000000..2ccd7c6abae --- /dev/null +++ b/lib/MC/MCCodeView.cpp @@ -0,0 +1,222 @@ +//===- MCCodeView.h - Machine Code CodeView support -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Holds state from .cv_file and .cv_loc directives for later emission. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCCodeView.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/Support/COFF.h" + +using namespace llvm; +using namespace llvm::codeview; + +CodeViewContext::CodeViewContext() {} + +CodeViewContext::~CodeViewContext() { + // If someone inserted strings into the string table but never actually + // emitted them somewhere, clean up the fragment. + if (!InsertedStrTabFragment) + delete StrTabFragment; +} + +/// This is a valid number for use with .cv_loc if we've already seen a .cv_file +/// for it. +bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const { + unsigned Idx = FileNumber - 1; + if (Idx < Filenames.size()) + return !Filenames[Idx].empty(); + return false; +} + +bool CodeViewContext::addFile(unsigned FileNumber, StringRef Filename) { + assert(FileNumber > 0); + Filename = addToStringTable(Filename); + unsigned Idx = FileNumber - 1; + if (Idx >= Filenames.size()) + Filenames.resize(Idx + 1); + + if (Filename.empty()) + Filename = ""; + + if (!Filenames[Idx].empty()) + return false; + + // FIXME: We should store the string table offset of the filename, rather than + // the filename itself for efficiency. + Filename = addToStringTable(Filename); + + Filenames[Idx] = Filename; + return true; +} + +MCDataFragment *CodeViewContext::getStringTableFragment() { + if (!StrTabFragment) { + StrTabFragment = new MCDataFragment(); + // Start a new string table out with a null byte. + StrTabFragment->getContents().push_back('\0'); + } + return StrTabFragment; +} + +StringRef CodeViewContext::addToStringTable(StringRef S) { + SmallVectorImpl &Contents = getStringTableFragment()->getContents(); + auto Insertion = + StringTable.insert(std::make_pair(S, unsigned(Contents.size()))); + // Return the string from the table, since it is stable. + S = Insertion.first->first(); + if (Insertion.second) { + // The string map key is always null terminated. + Contents.append(S.begin(), S.end() + 1); + } + return S; +} + +unsigned CodeViewContext::getStringTableOffset(StringRef S) { + // A string table offset of zero is always the empty string. + if (S.empty()) + return 0; + auto I = StringTable.find(S); + assert(I != StringTable.end()); + return I->second; +} + +void CodeViewContext::emitStringTable(MCObjectStreamer &OS) { + MCContext &Ctx = OS.getContext(); + MCSymbol *StringBegin = Ctx.createTempSymbol("strtab_begin"), + *StringEnd = Ctx.createTempSymbol("strtab_end"); + + OS.EmitIntValue(unsigned(ModuleSubstreamKind::StringTable), 4); + OS.emitAbsoluteSymbolDiff(StringEnd, StringBegin, 4); + OS.EmitLabel(StringBegin); + + // Put the string table data fragment here, if we haven't already put it + // somewhere else. If somebody wants two string tables in their .s file, one + // will just be empty. + if (!InsertedStrTabFragment) { + OS.insert(getStringTableFragment()); + InsertedStrTabFragment = true; + } + + OS.EmitValueToAlignment(4, 0); + + OS.EmitLabel(StringEnd); +} + +void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) { + MCContext &Ctx = OS.getContext(); + MCSymbol *FileBegin = Ctx.createTempSymbol("filechecksums_begin"), + *FileEnd = Ctx.createTempSymbol("filechecksums_end"); + + OS.EmitIntValue(unsigned(ModuleSubstreamKind::FileChecksums), 4); + OS.emitAbsoluteSymbolDiff(FileEnd, FileBegin, 4); + OS.EmitLabel(FileBegin); + + // Emit an array of FileChecksum entries. We index into this table using the + // user-provided file number. Each entry is currently 8 bytes, as we don't + // emit checksums. + for (StringRef Filename : Filenames) { + OS.EmitIntValue(getStringTableOffset(Filename), 4); + // Zero the next two fields and align back to 4 bytes. This indicates that + // no checksum is present. + OS.EmitIntValue(0, 4); + } + + OS.EmitLabel(FileEnd); +} + +void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS, + unsigned FuncId, + const MCSymbol *FuncBegin, + const MCSymbol *FuncEnd) { + MCContext &Ctx = OS.getContext(); + MCSymbol *LineBegin = Ctx.createTempSymbol("linetable_begin"), + *LineEnd = Ctx.createTempSymbol("linetable_end"); + + OS.EmitIntValue(unsigned(ModuleSubstreamKind::Lines), 4); + OS.emitAbsoluteSymbolDiff(LineEnd, LineBegin, 4); + OS.EmitLabel(LineBegin); + OS.EmitCOFFSecRel32(FuncBegin); + OS.EmitCOFFSectionIndex(FuncBegin); + + // Actual line info. + ArrayRef Locs = getFunctionLineEntries(FuncId); + bool HaveColumns = any_of(Locs, [](const MCCVLineEntry &LineEntry) { + return LineEntry.getColumn() != 0; + }); + OS.EmitIntValue(HaveColumns ? codeview::LineFlags::HaveColumns : 0, 2); + OS.emitAbsoluteSymbolDiff(FuncEnd, FuncBegin, 4); + + for (auto I = Locs.begin(), E = Locs.end(); I != E;) { + // Emit a file segment for the run of locations that share a file id. + unsigned CurFileNum = I->getFileNum(); + auto FileSegEnd = + std::find_if(I, E, [CurFileNum](const MCCVLineEntry &Loc) { + return Loc.getFileNum() != CurFileNum; + }); + unsigned EntryCount = FileSegEnd - I; + OS.AddComment("Segment for file '" + Twine(Filenames[CurFileNum - 1]) + + "' begins"); + OS.EmitIntValue(8 * (CurFileNum - 1), 4); + OS.EmitIntValue(EntryCount, 4); + uint32_t SegmentSize = 12; + SegmentSize += 8 * EntryCount; + if (HaveColumns) + SegmentSize += 4 * EntryCount; + OS.EmitIntValue(SegmentSize, 4); + + for (auto J = I; J != FileSegEnd; ++J) { + OS.emitAbsoluteSymbolDiff(J->getLabel(), FuncBegin, 4); + unsigned LineData = J->getLine(); + if (J->isStmt()) + LineData |= codeview::LineInfo::StatementFlag; + OS.EmitIntValue(LineData, 4); + } + if (HaveColumns) { + for (auto J = I; J != FileSegEnd; ++J) { + OS.EmitIntValue(J->getColumn(), 2); + OS.EmitIntValue(0, 2); + } + } + I = FileSegEnd; + } + OS.EmitLabel(LineEnd); +} + +// +// This is called when an instruction is assembled into the specified section +// and if there is information from the last .cv_loc directive that has yet to have +// a line entry made for it is made. +// +void MCCVLineEntry::Make(MCObjectStreamer *MCOS) { + if (!MCOS->getContext().getCVLocSeen()) + return; + + // Create a symbol at in the current section for use in the line entry. + MCSymbol *LineSym = MCOS->getContext().createTempSymbol(); + // Set the value of the symbol to use for the MCCVLineEntry. + MCOS->EmitLabel(LineSym); + + // Get the current .loc info saved in the context. + const MCCVLoc &CVLoc = MCOS->getContext().getCurrentCVLoc(); + + // Create a (local) line entry with the symbol and the current .loc info. + MCCVLineEntry LineEntry(LineSym, CVLoc); + + // clear CVLocSeen saying the current .loc info is now used. + MCOS->getContext().clearCVLocSeen(); + + // Add the line entry to this section's entries. + MCOS->getContext().getCVContext().addLineEntry(LineEntry); +} diff --git a/lib/MC/MCContext.cpp b/lib/MC/MCContext.cpp index b5ad518d033..c5243ef62c9 100644 --- a/lib/MC/MCContext.cpp +++ b/lib/MC/MCContext.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeView.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCLabel.h" #include "llvm/MC/MCObjectFileInfo.h" @@ -90,6 +91,8 @@ void MCContext::reset() { DwarfCompileUnitID = 0; CurrentDwarfLoc = MCDwarfLoc(0, 0, 0, DWARF2_FLAG_IS_STMT, 0, 0); + CVContext.reset(); + MachOUniquingMap.clear(); ELFUniquingMap.clear(); COFFUniquingMap.clear(); @@ -474,6 +477,20 @@ void MCContext::finalizeDwarfSections(MCStreamer &MCOS) { [&](MCSection *Sec) { return !MCOS.mayHaveInstructions(*Sec); }); } +CodeViewContext &MCContext::getCVContext() { + if (!CVContext.get()) + CVContext.reset(new CodeViewContext); + return *CVContext.get(); +} + +unsigned MCContext::getCVFile(StringRef FileName, unsigned FileNumber) { + return getCVContext().addFile(FileNumber, FileName) ? FileNumber : 0; +} + +bool MCContext::isValidCVFileNumber(unsigned FileNumber) { + return getCVContext().isValidFileNumber(FileNumber); +} + //===----------------------------------------------------------------------===// // Error Reporting //===----------------------------------------------------------------------===// diff --git a/lib/MC/MCObjectStreamer.cpp b/lib/MC/MCObjectStreamer.cpp index 39755e5b0a8..71d578aca9d 100644 --- a/lib/MC/MCObjectStreamer.cpp +++ b/lib/MC/MCObjectStreamer.cpp @@ -125,6 +125,7 @@ void MCObjectStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); + MCCVLineEntry::Make(this); MCDwarfLineEntry::Make(this, getCurrentSection().first); // Avoid fixups when possible. @@ -232,6 +233,7 @@ void MCObjectStreamer::EmitInstruction(const MCInst &Inst, // Now that a machine instruction has been assembled into this section, make // a line entry for any .loc directive that has been seen. + MCCVLineEntry::Make(this); MCDwarfLineEntry::Make(this, getCurrentSection().first); // If this instruction doesn't need relaxation, just emit it as data. @@ -362,7 +364,36 @@ void MCObjectStreamer::EmitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, insert(new MCDwarfCallFrameFragment(*AddrDelta)); } +void MCObjectStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, + unsigned Line, unsigned Column, + bool PrologueEnd, bool IsStmt, + StringRef FileName) { + // In case we see two .cv_loc directives in a row, make sure the + // first one gets a line entry. + MCCVLineEntry::Make(this); + + this->MCStreamer::EmitCVLocDirective(FunctionId, FileNo, Line, Column, + PrologueEnd, IsStmt, FileName); +} + +void MCObjectStreamer::EmitCVLinetableDirective(unsigned FunctionId, + const MCSymbol *Begin, + const MCSymbol *End) { + getContext().getCVContext().emitLineTableForFunction(*this, FunctionId, Begin, + End); + this->MCStreamer::EmitCVLinetableDirective(FunctionId, Begin, End); +} + +void MCObjectStreamer::EmitCVStringTableDirective() { + getContext().getCVContext().emitStringTable(*this); +} +void MCObjectStreamer::EmitCVFileChecksumsDirective() { + getContext().getCVContext().emitFileChecksums(*this); +} + + void MCObjectStreamer::EmitBytes(StringRef Data) { + MCCVLineEntry::Make(this); MCDwarfLineEntry::Make(this, getCurrentSection().first); MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index b1814f8c8ff..360de5db883 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -357,6 +357,8 @@ private: DK_IFNB, DK_IFC, DK_IFEQS, DK_IFNC, DK_IFNES, DK_IFDEF, DK_IFNDEF, DK_IFNOTDEF, DK_ELSEIF, DK_ELSE, DK_ENDIF, DK_SPACE, DK_SKIP, DK_FILE, DK_LINE, DK_LOC, DK_STABS, + DK_CV_FILE, DK_CV_LOC, DK_CV_LINETABLE, DK_CV_STRINGTABLE, + DK_CV_FILECHECKSUMS, DK_CFI_SECTIONS, DK_CFI_STARTPROC, DK_CFI_ENDPROC, DK_CFI_DEF_CFA, DK_CFI_DEF_CFA_OFFSET, DK_CFI_ADJUST_CFA_OFFSET, DK_CFI_DEF_CFA_REGISTER, DK_CFI_OFFSET, DK_CFI_REL_OFFSET, DK_CFI_PERSONALITY, DK_CFI_LSDA, @@ -394,6 +396,13 @@ private: bool parseDirectiveLoc(); bool parseDirectiveStabs(); + // ".cv_file", ".cv_loc", ".cv_linetable" + bool parseDirectiveCVFile(); + bool parseDirectiveCVLoc(); + bool parseDirectiveCVLinetable(); + bool parseDirectiveCVStringTable(); + bool parseDirectiveCVFileChecksums(); + // .cfi directives bool parseDirectiveCFIRegister(SMLoc DirectiveLoc); bool parseDirectiveCFIWindowSave(); @@ -1638,6 +1647,16 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info, return parseDirectiveLoc(); case DK_STABS: return parseDirectiveStabs(); + case DK_CV_FILE: + return parseDirectiveCVFile(); + case DK_CV_LOC: + return parseDirectiveCVLoc(); + case DK_CV_LINETABLE: + return parseDirectiveCVLinetable(); + case DK_CV_STRINGTABLE: + return parseDirectiveCVStringTable(); + case DK_CV_FILECHECKSUMS: + return parseDirectiveCVFileChecksums(); case DK_CFI_SECTIONS: return parseDirectiveCFISections(); case DK_CFI_STARTPROC: @@ -3070,6 +3089,156 @@ bool AsmParser::parseDirectiveStabs() { return TokError("unsupported directive '.stabs'"); } +/// parseDirectiveCVFile +/// ::= .cv_file number filename +bool AsmParser::parseDirectiveCVFile() { + SMLoc FileNumberLoc = getLexer().getLoc(); + if (getLexer().isNot(AsmToken::Integer)) + return TokError("expected file number in '.cv_file' directive"); + + int64_t FileNumber = getTok().getIntVal(); + Lex(); + + if (FileNumber < 1) + return TokError("file number less than one"); + + if (getLexer().isNot(AsmToken::String)) + return TokError("unexpected token in '.cv_file' directive"); + + // Usually the directory and filename together, otherwise just the directory. + // Allow the strings to have escaped octal character sequence. + std::string Filename; + if (parseEscapedString(Filename)) + return true; + Lex(); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return TokError("unexpected token in '.cv_file' directive"); + + if (getStreamer().EmitCVFileDirective(FileNumber, Filename) == 0) + Error(FileNumberLoc, "file number already allocated"); + + return false; +} + +/// parseDirectiveCVLoc +/// ::= .cv_loc FunctionId FileNumber [LineNumber] [ColumnPos] [prologue_end] +/// [is_stmt VALUE] +/// The first number is a file number, must have been previously assigned with +/// a .file directive, the second number is the line number and optionally the +/// third number is a column position (zero if not specified). The remaining +/// optional items are .loc sub-directives. +bool AsmParser::parseDirectiveCVLoc() { + if (getLexer().isNot(AsmToken::Integer)) + return TokError("unexpected token in '.cv_loc' directive"); + + int64_t FunctionId = getTok().getIntVal(); + if (FunctionId < 0) + return TokError("function id less than zero in '.cv_loc' directive"); + Lex(); + + int64_t FileNumber = getTok().getIntVal(); + if (FileNumber < 1) + return TokError("file number less than one in '.cv_loc' directive"); + if (!getContext().isValidCVFileNumber(FileNumber)) + return TokError("unassigned file number in '.cv_loc' directive"); + Lex(); + + int64_t LineNumber = 0; + if (getLexer().is(AsmToken::Integer)) { + LineNumber = getTok().getIntVal(); + if (LineNumber < 0) + return TokError("line number less than zero in '.cv_loc' directive"); + Lex(); + } + + int64_t ColumnPos = 0; + if (getLexer().is(AsmToken::Integer)) { + ColumnPos = getTok().getIntVal(); + if (ColumnPos < 0) + return TokError("column position less than zero in '.cv_loc' directive"); + Lex(); + } + + bool PrologueEnd = false; + uint64_t IsStmt = 0; + while (getLexer().isNot(AsmToken::EndOfStatement)) { + StringRef Name; + SMLoc Loc = getTok().getLoc(); + if (parseIdentifier(Name)) + return TokError("unexpected token in '.cv_loc' directive"); + + if (Name == "prologue_end") + PrologueEnd = true; + else if (Name == "is_stmt") { + Loc = getTok().getLoc(); + const MCExpr *Value; + if (parseExpression(Value)) + return true; + // The expression must be the constant 0 or 1. + IsStmt = ~0ULL; + if (const auto *MCE = dyn_cast(Value)) + IsStmt = MCE->getValue(); + + if (IsStmt > 1) + return Error(Loc, "is_stmt value not 0 or 1"); + } else { + return Error(Loc, "unknown sub-directive in '.cv_loc' directive"); + } + } + + getStreamer().EmitCVLocDirective(FunctionId, FileNumber, LineNumber, + ColumnPos, PrologueEnd, IsStmt, StringRef()); + return false; +} + +/// parseDirectiveCVLinetable +/// ::= .cv_linetable FunctionId, FnStart, FnEnd +bool AsmParser::parseDirectiveCVLinetable() { + int64_t FunctionId = getTok().getIntVal(); + if (FunctionId < 0) + return TokError("function id less than zero in '.cv_linetable' directive"); + Lex(); + + if (Lexer.isNot(AsmToken::Comma)) + return TokError("unexpected token in '.cv_linetable' directive"); + Lex(); + + SMLoc Loc = getLexer().getLoc(); + StringRef FnStartName; + if (parseIdentifier(FnStartName)) + return Error(Loc, "expected identifier in directive"); + + if (Lexer.isNot(AsmToken::Comma)) + return TokError("unexpected token in '.cv_linetable' directive"); + Lex(); + + Loc = getLexer().getLoc(); + StringRef FnEndName; + if (parseIdentifier(FnEndName)) + return Error(Loc, "expected identifier in directive"); + + MCSymbol *FnStartSym = getContext().getOrCreateSymbol(FnStartName); + MCSymbol *FnEndSym = getContext().getOrCreateSymbol(FnEndName); + + getStreamer().EmitCVLinetableDirective(FunctionId, FnStartSym, FnEndSym); + return false; +} + +/// parseDirectiveCVStringTable +/// ::= .cv_stringtable +bool AsmParser::parseDirectiveCVStringTable() { + getStreamer().EmitCVStringTableDirective(); + return false; +} + +/// parseDirectiveCVFileChecksums +/// ::= .cv_filechecksums +bool AsmParser::parseDirectiveCVFileChecksums() { + getStreamer().EmitCVFileChecksumsDirective(); + return false; +} + /// parseDirectiveCFISections /// ::= .cfi_sections section [, section] bool AsmParser::parseDirectiveCFISections() { @@ -4381,6 +4550,11 @@ void AsmParser::initializeDirectiveKindMap() { DirectiveKindMap[".line"] = DK_LINE; DirectiveKindMap[".loc"] = DK_LOC; DirectiveKindMap[".stabs"] = DK_STABS; + DirectiveKindMap[".cv_file"] = DK_CV_FILE; + DirectiveKindMap[".cv_loc"] = DK_CV_LOC; + DirectiveKindMap[".cv_linetable"] = DK_CV_LINETABLE; + DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE; + DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS; DirectiveKindMap[".sleb128"] = DK_SLEB128; DirectiveKindMap[".uleb128"] = DK_ULEB128; DirectiveKindMap[".cfi_sections"] = DK_CFI_SECTIONS; diff --git a/lib/MC/MCStreamer.cpp b/lib/MC/MCStreamer.cpp index 836b4054464..adae5d73ce5 100644 --- a/lib/MC/MCStreamer.cpp +++ b/lib/MC/MCStreamer.cpp @@ -180,6 +180,22 @@ void MCStreamer::EnsureValidDwarfFrame() { report_fatal_error("No open frame"); } +unsigned MCStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename) { + return getContext().getCVFile(Filename, FileNo); +} + +void MCStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, + unsigned Line, unsigned Column, + bool PrologueEnd, bool IsStmt, + StringRef FileName) { + getContext().setCurrentCVLoc(FunctionId, FileNo, Line, Column, PrologueEnd, + IsStmt); +} + +void MCStreamer::EmitCVLinetableDirective(unsigned FunctionId, + const MCSymbol *Begin, + const MCSymbol *End) {} + void MCStreamer::EmitEHSymAttributes(const MCSymbol *Symbol, MCSymbol *EHSymbol) { } diff --git a/lib/MC/StringTableBuilder.cpp b/lib/MC/StringTableBuilder.cpp index 80e552287b3..f50e098e6e4 100644 --- a/lib/MC/StringTableBuilder.cpp +++ b/lib/MC/StringTableBuilder.cpp @@ -16,7 +16,22 @@ using namespace llvm; -StringTableBuilder::StringTableBuilder(Kind K) : K(K) {} +StringTableBuilder::StringTableBuilder(Kind K) : K(K) { + // Account for leading bytes in table so that offsets returned from add are + // correct. + switch (K) { + case RAW: + Size = 0; + break; + case MachO: + case ELF: + Size = 1; + break; + case WinCOFF: + Size = 4; + break; + } +} typedef std::pair StringPair; @@ -62,13 +77,32 @@ tailcall: } void StringTableBuilder::finalize() { - std::vector *> Strings; + finalizeStringTable(/*Optimize=*/true); +} + +void StringTableBuilder::finalizeInOrder() { + finalizeStringTable(/*Optimize=*/false); +} + +void StringTableBuilder::finalizeStringTable(bool Optimize) { + typedef std::pair StringOffsetPair; + std::vector Strings; Strings.reserve(StringIndexMap.size()); - for (std::pair &P : StringIndexMap) + for (StringOffsetPair &P : StringIndexMap) Strings.push_back(&P); - if (!Strings.empty()) - multikey_qsort(&Strings[0], &Strings[0] + Strings.size(), 0); + if (!Strings.empty()) { + // If we're optimizing, sort by name. If not, sort by previously assigned + // offset. + if (Optimize) { + multikey_qsort(&Strings[0], &Strings[0] + Strings.size(), 0); + } else { + std::sort(Strings.begin(), Strings.end(), + [](const StringOffsetPair *LHS, const StringOffsetPair *RHS) { + return LHS->second < RHS->second; + }); + } + } switch (K) { case RAW: @@ -85,17 +119,22 @@ void StringTableBuilder::finalize() { } StringRef Previous; - for (std::pair *P : Strings) { + for (StringOffsetPair *P : Strings) { StringRef S = P->first; if (K == WinCOFF) assert(S.size() > COFF::NameSize && "Short string in COFF string table!"); - if (Previous.endswith(S)) { + if (Optimize && Previous.endswith(S)) { P->second = StringTable.size() - S.size() - (K != RAW); continue; } - P->second = StringTable.size(); + if (Optimize) + P->second = StringTable.size(); + else + assert(P->second == StringTable.size() && + "different strtab offset after finalization"); + StringTable += S; if (K != RAW) StringTable += '\x00'; diff --git a/test/DebugInfo/COFF/asm.ll b/test/DebugInfo/COFF/asm.ll index e4daeda3394..bb593796238 100644 --- a/test/DebugInfo/COFF/asm.ll +++ b/test/DebugInfo/COFF/asm.ll @@ -13,11 +13,11 @@ ; 6 } ; X86-LABEL: _f: -; X86: # BB -; X86-NEXT: [[ASM_LINE:^L.*]]:{{$}} -; X86: [[CALL_LINE:^L.*]]:{{$}} +; X86: .cv_file 1 "D:\\asm.c" +; X86: .cv_loc 0 1 4 0 is_stmt 0 +; X86: .cv_loc 0 1 5 0 ; X86: calll _g -; X86-NEXT: [[RETURN_STMT:.*]]: +; X86: .cv_loc 0 1 6 0 ; X86: ret ; X86-NEXT: [[END_OF_F:^L.*]]: ; @@ -45,48 +45,15 @@ ; Padding ; X86-NEXT: .zero 3 ; Line table -; X86-NEXT: .long 242 -; X86-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X86-NEXT: [[F2_START]]: -; X86-NEXT: .secrel32 _f -; X86-NEXT: .secidx _f -; X86-NEXT: .short 1 -; X86-NEXT: .long [[END_OF_F]]-_f -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 3 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[ASM_LINE]]-_f -; X86-NEXT: .long -2147483644 -; X86-NEXT: .long [[CALL_LINE]]-_f -; X86-NEXT: .long -2147483643 -; X86-NEXT: .long [[RETURN_STMT]]-_f -; X86-NEXT: .long -2147483642 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; X86-NEXT: [[F2_END]]: +; X86-NEXT: .cv_linetable 0, _f, [[END_OF_F]] ; File index to string table offset subsection -; X86-NEXT: .long 244 -; X86-NEXT: .long 8 -; X86-NEXT: .long 1 -; X86-NEXT: .long 0 +; X86-NEXT: .cv_filechecksums ; String table -; X86-NEXT: .long 243 -; X86-NEXT: .long 10 -; X86-NEXT: .byte 0 -; X86-NEXT: .ascii "D:\\asm.c" -; X86-NEXT: .byte 0 -; Padding -; X86-NEXT: .zero 2 +; X86-NEXT: .cv_stringtable ; OBJ32: Section { ; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ32: Characteristics [ (0x42100040) +; OBJ32: Characteristics [ (0x42300040) ; OBJ32: ] ; OBJ32: Relocations [ ; OBJ32-NEXT: 0x2C IMAGE_REL_I386_SECREL _f @@ -107,7 +74,7 @@ ; OBJ32-NEXT: ] ; OBJ32: FunctionLineTable [ ; OBJ32-NEXT: Name: _f -; OBJ32-NEXT: Flags: 0x1 +; OBJ32-NEXT: Flags: 0x0 ; OBJ32-NEXT: CodeSize: 0x6 ; OBJ32-NEXT: FilenameSegment [ ; OBJ32-NEXT: Filename: D:\asm.c @@ -118,34 +85,27 @@ ; OBJ32-NEXT: LineNumberStart: 4 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: +0x0 [ ; OBJ32-NEXT: LineNumberStart: 5 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: +0x5 [ ; OBJ32-NEXT: LineNumberStart: 6 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: ] ; X64-LABEL: f: -; X64-NEXT: .L{{.*}}:{{$}} -; X64-NEXT: [[START:.*]]:{{$}} -; X64: # BB +; X64: .cv_file 1 "D:\\asm.c" +; X64: .cv_loc 0 1 3 0 is_stmt 0 ; X64: subq $40, %rsp -; X64-NEXT: [[ASM_LINE:.*]]:{{$}} -; X64: [[CALL_LINE:.*]]:{{$}} +; X64: .cv_loc 0 1 4 0 +; X64: .cv_loc 0 1 5 0 ; X64: callq g -; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64: .cv_loc 0 1 6 0 ; X64: addq $40, %rsp ; X64-NEXT: ret ; X64-NEXT: [[END_OF_F:.*]]: @@ -174,52 +134,15 @@ ; Padding ; X64-NEXT: .zero 3 ; Line table -; X64-NEXT: .long 242 -; X64-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X64-NEXT: [[F2_START]]: -; X64-NEXT: .secrel32 f -; X64-NEXT: .secidx f -; X64-NEXT: .short 1 -; X64-NEXT: .long [[END_OF_F]]-f -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 0 -; X64-NEXT: .long 4 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[START]]-f -; X64-NEXT: .long -2147483645 -; X64-NEXT: .long [[ASM_LINE]]-f -; X64-NEXT: .long -2147483644 -; X64-NEXT: .long [[CALL_LINE]]-f -; X64-NEXT: .long -2147483643 -; X64-NEXT: .long [[EPILOG_AND_RET]]-f -; X64-NEXT: .long -2147483642 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; X64-NEXT: [[F2_END]]: +; X64-NEXT: .cv_linetable 0, f, [[END_OF_F]] ; File index to string table offset subsection -; X64-NEXT: .long 244 -; X64-NEXT: .long 8 -; X64-NEXT: .long 1 -; X64-NEXT: .long 0 +; X64-NEXT: .cv_filechecksums ; String table -; X64-NEXT: .long 243 -; X64-NEXT: .long 10 -; X64-NEXT: .byte 0 -; X64-NEXT: .ascii "D:\\asm.c" -; X64-NEXT: .byte 0 -; Padding -; X64-NEXT: .zero 2 +; X64-NEXT: .cv_stringtable ; OBJ64: Section { ; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ64: Characteristics [ (0x42100040) +; OBJ64: Characteristics [ (0x42300040) ; OBJ64: ] ; OBJ64: Relocations [ ; OBJ64-NEXT: 0x2C IMAGE_REL_AMD64_SECREL f @@ -239,7 +162,7 @@ ; OBJ64-NEXT: ] ; OBJ64: FunctionLineTable [ ; OBJ64-NEXT: Name: f -; OBJ64-NEXT: Flags: 0x1 +; OBJ64-NEXT: Flags: 0x0 ; OBJ64-NEXT: CodeSize: 0xE ; OBJ64-NEXT: FilenameSegment [ ; OBJ64-NEXT: Filename: D:\asm.c @@ -249,29 +172,21 @@ ; OBJ64-NEXT: LineNumberStart: 3 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: +0x4 [ ; OBJ64-NEXT: LineNumberStart: 4 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: +0x4 [ ; OBJ64-NEXT: LineNumberStart: 5 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: +0x9 [ ; OBJ64-NEXT: LineNumberStart: 6 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] diff --git a/test/DebugInfo/COFF/multifile.ll b/test/DebugInfo/COFF/multifile.ll index 6c52259aec7..4de49881454 100644 --- a/test/DebugInfo/COFF/multifile.ll +++ b/test/DebugInfo/COFF/multifile.ll @@ -18,13 +18,15 @@ ; X86-LABEL: _f: ; X86: # BB -; X86-NEXT: [[CALL_LINE_1:.*]]:{{$}} +; X86: .cv_file 1 "D:\\one.c" +; X86: .cv_loc 0 1 1 0 is_stmt 0 # one.c:1:0 ; X86: calll _g -; X86-NEXT: [[CALL_LINE_2:.*]]:{{$}} +; X86: .cv_file 2 "D:\\two.c" +; X86: .cv_loc 0 2 2 0 # two.c:2:0 ; X86: calll _g -; X86-NEXT: [[CALL_LINE_3:.*]]:{{$}} +; X86: .cv_loc 0 1 7 0 # one.c:7:0 ; X86: calll _g -; X86-NEXT: [[RETURN_STMT:.*]]: +; X86: .cv_loc 0 1 8 0 # one.c:8:0 ; X86: ret ; X86-NEXT: [[END_OF_F:.*]]: ; @@ -52,68 +54,15 @@ ; Padding ; X86-NEXT: .zero 3 ; Line table -; X86-NEXT: .long 242 -; X86-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X86-NEXT: [[F2_START]]: -; X86-NEXT: .secrel32 _f -; X86-NEXT: .secidx _f -; X86-NEXT: .short 1 -; X86-NEXT: .long [[END_OF_F]]-_f -; Segment for file 'D:\\one.c' begins -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 1 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[CALL_LINE_1]]-_f -; X86-NEXT: .long -2147483647 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; Segment for file 'D:\\two.c' begins -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 8 -; X86-NEXT: .long 1 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[CALL_LINE_2]]-_f -; X86-NEXT: .long -2147483646 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; A new segment for file 'D:\\one.c' begins -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 2 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[CALL_LINE_3]]-_f -; X86-NEXT: .long -2147483641 -; X86-NEXT: .long [[RETURN_STMT]]-_f -; X86-NEXT: .long -2147483640 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; X86-NEXT: [[F2_END]]: +; X86-NEXT: .cv_linetable 0, _f, [[END_OF_F]] ; File index to string table offset subsection -; X86-NEXT: .long 244 -; X86-NEXT: .long 16 -; X86-NEXT: .long 1 -; X86-NEXT: .long 0 -; X86-NEXT: .long 10 -; X86-NEXT: .long 0 +; X86-NEXT: .cv_filechecksums ; String table -; X86-NEXT: .long 243 -; X86-NEXT: .long 19 -; X86-NEXT: .byte 0 -; X86-NEXT: .ascii "D:\\one.c" -; X86-NEXT: .byte 0 -; X86-NEXT: .ascii "D:\\two.c" -; X86-NEXT: .byte 0 -; X86-NEXT: .zero 1 +; X86-NEXT: .cv_stringtable ; OBJ32: Section { ; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ32: Characteristics [ (0x42100040) +; OBJ32: Characteristics [ (0x42300040) ; OBJ32: ] ; OBJ32: Relocations [ ; OBJ32-NEXT: 0x2C IMAGE_REL_I386_SECREL _f @@ -133,7 +82,7 @@ ; OBJ32-NEXT: ] ; OBJ32: FunctionLineTable [ ; OBJ32-NEXT: Name: _f -; OBJ32-NEXT: Flags: 0x1 +; OBJ32-NEXT: Flags: 0x0 ; OBJ32-NEXT: CodeSize: 0x10 ; OBJ32-NEXT: FilenameSegment [ ; OBJ32-NEXT: Filename: D:\one.c @@ -141,8 +90,6 @@ ; OBJ32-NEXT: LineNumberStart: 1 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: ] ; OBJ32-NEXT: FilenameSegment [ @@ -151,8 +98,6 @@ ; OBJ32-NEXT: LineNumberStart: 2 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: ] ; OBJ32-NEXT: FilenameSegment [ @@ -161,31 +106,30 @@ ; OBJ32-NEXT: LineNumberStart: 7 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: +0xF [ ; OBJ32-NEXT: LineNumberStart: 8 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 -; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: ] ; OBJ32-NEXT: ] ; X64-LABEL: f: ; X64-NEXT: .L{{.*}}:{{$}} -; X64-NEXT: [[START:.*]]:{{$}} +; X64: .cv_file 1 "D:\\input.c" +; X64: .cv_loc 0 1 3 0 is_stmt 0 # input.c:3:0 ; X64: # BB ; X64: subq $40, %rsp -; X64-NEXT: [[CALL_LINE_1:.*]]:{{$}} +; X64: .cv_file 2 "D:\\one.c" +; X64: .cv_loc 0 2 1 0 # one.c:1:0 ; X64: callq g -; X64-NEXT: [[CALL_LINE_2:.*]]:{{$}} +; X64: .cv_file 3 "D:\\two.c" +; X64: .cv_loc 0 3 2 0 # two.c:2:0 ; X64: callq g -; X64-NEXT: [[CALL_LINE_3:.*]]:{{$}} +; X64: .cv_loc 0 2 7 0 # one.c:7:0 ; X64: callq g -; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64: .cv_loc 0 2 8 0 # one.c:8:0 ; X64: addq $40, %rsp ; X64-NEXT: ret ; X64-NEXT: [[END_OF_F:.*]]: @@ -213,83 +157,13 @@ ; X64-NEXT: [[F1_END]]: ; Padding ; X64-NEXT: .zero 3 -; Line table -; X64-NEXT: .long 242 -; X64-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X64-NEXT: [[F2_START]]: -; X64-NEXT: .secrel32 f -; X64-NEXT: .secidx f -; X64-NEXT: .short 1 -; X64-NEXT: .long [[END_OF_F]]-f -; Segment for file 'D:\\input.c' begins -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 0 -; X64-NEXT: .long 1 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[START]]-f -; X64-NEXT: .long -2147483645 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; Segment for file 'D:\\one.c' begins -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 8 -; X64-NEXT: .long 1 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[CALL_LINE_1]]-f -; X64-NEXT: .long -2147483647 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; Segment for file 'D:\\two.c' begins -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 16 -; X64-NEXT: .long 1 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[CALL_LINE_2]]-f -; X64-NEXT: .long -2147483646 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; A new segment for file 'D:\\one.c' begins -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 8 -; X64-NEXT: .long 2 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[CALL_LINE_3]]-f -; X64-NEXT: .long -2147483641 -; X64-NEXT: .long [[EPILOG_AND_RET]]-f -; X64-NEXT: .long -2147483640 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; X64-NEXT: [[F2_END]]: -; File index to string table offset subsection -; X64-NEXT: .long 244 -; X64-NEXT: .long 24 -; X64-NEXT: .long 1 -; X64-NEXT: .long 0 -; X64-NEXT: .long 12 -; X64-NEXT: .long 0 -; X64-NEXT: .long 21 -; X64-NEXT: .long 0 -; String table -; X64-NEXT: .long 243 -; X64-NEXT: .long 30 -; X64-NEXT: .byte 0 -; X64-NEXT: .ascii "D:\\input.c" -; X64-NEXT: .byte 0 -; X64-NEXT: .ascii "D:\\one.c" -; X64-NEXT: .byte 0 -; X64-NEXT: .ascii "D:\\two.c" -; X64-NEXT: .byte 0 -; X64-NEXT: .zero 2 +; X64: .cv_linetable 0, f, [[END_OF_F]] +; X64: .cv_filechecksums +; X64: .cv_stringtable ; OBJ64: Section { ; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ64: Characteristics [ (0x42100040) +; OBJ64: Characteristics [ (0x42300040) ; OBJ64: ] ; OBJ64: Relocations [ ; OBJ64-NEXT: 0x2C IMAGE_REL_AMD64_SECREL f @@ -309,7 +183,7 @@ ; OBJ64-NEXT: ] ; OBJ64: FunctionLineTable [ ; OBJ64-NEXT: Name: f -; OBJ64-NEXT: Flags: 0x1 +; OBJ64-NEXT: Flags: 0x0 ; OBJ64-NEXT: CodeSize: 0x18 ; OBJ64-NEXT: FilenameSegment [ ; OBJ64-NEXT: Filename: D:\input.c @@ -317,8 +191,6 @@ ; OBJ64-NEXT: LineNumberStart: 3 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] ; OBJ64-NEXT: FilenameSegment [ @@ -327,8 +199,6 @@ ; OBJ64-NEXT: LineNumberStart: 1 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] ; OBJ64-NEXT: FilenameSegment [ @@ -337,8 +207,6 @@ ; OBJ64-NEXT: LineNumberStart: 2 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] ; OBJ64-NEXT: FilenameSegment [ @@ -347,15 +215,11 @@ ; OBJ64-NEXT: LineNumberStart: 7 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: +0x13 [ ; OBJ64-NEXT: LineNumberStart: 8 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 -; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] ; OBJ64-NEXT: ] diff --git a/test/DebugInfo/COFF/multifunction.ll b/test/DebugInfo/COFF/multifunction.ll index ff8b924bed3..83ce9c77fe3 100644 --- a/test/DebugInfo/COFF/multifunction.ll +++ b/test/DebugInfo/COFF/multifunction.ll @@ -24,29 +24,30 @@ ; X86-LABEL: _x: ; X86: # BB -; X86-NEXT: [[X_CALL:.*]]:{{$}} +; X86: .cv_file 1 "D:\\source.c" +; X86: .cv_loc 0 1 4 42 is_stmt 0 # source.c:4:42 ; X86: calll _z -; X86-NEXT: [[X_RETURN:.*]]: +; X86: .cv_loc 0 1 5 43 # source.c:5:43 ; X86: ret ; X86-NEXT: [[END_OF_X:.*]]: ; ; X86-LABEL: _y: ; X86: # BB -; X86-NEXT: [[Y_CALL:.*]]:{{$}} +; X86: .cv_loc 1 1 8 52 # source.c:8:52 ; X86: calll _z -; X86-NEXT: [[Y_RETURN:.*]]: +; X86: .cv_loc 1 1 9 53 # source.c:9:53 ; X86: ret ; X86-NEXT: [[END_OF_Y:.*]]: ; ; X86-LABEL: _f: ; X86: # BB -; X86-NEXT: [[F_CALLS_X:.*]]:{{$}} +; X86: .cv_loc 2 1 12 62 # source.c:12:62 ; X86: calll _x -; X86-NEXT: [[F_CALLS_Y:.*]]: +; X86: .cv_loc 2 1 13 63 # source.c:13:63 ; X86: calll _y -; X86-NEXT: [[F_CALLS_Z:.*]]: +; X86: .cv_loc 2 1 14 72 # source.c:14:72 ; X86: calll _z -; X86-NEXT: [[F_RETURN:.*]]: +; X86: .cv_loc 2 1 15 73 # source.c:15:73 ; X86: ret ; X86-NEXT: [[END_OF_F:.*]]: ; @@ -74,27 +75,7 @@ ; Padding ; X86-NEXT: .zero 3 ; Line table subsection for x -; X86-NEXT: .long 242 -; X86-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X86-NEXT: [[F2_START]]: -; X86-NEXT: .secrel32 _x -; X86-NEXT: .secidx _x -; X86-NEXT: .short 1 -; X86-NEXT: .long [[END_OF_X]]-_x -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 2 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[X_CALL]]-_x -; X86-NEXT: .long -2147483644 -; X86-NEXT: .long [[X_RETURN]]-_x -; X86-NEXT: .long -2147483643 -; X86-NEXT: .short 42 -; X86-NEXT: .short 0 -; X86-NEXT: .short 43 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; X86-NEXT: [[F2_END]]: +; X86: .cv_linetable 0, _x, [[END_OF_X]] ; Symbol subsection for y ; X86-NEXT: .long 241 ; X86-NEXT: .long [[F1_END:.*]]-[[F1_START:.*]] @@ -117,27 +98,7 @@ ; Padding ; X86-NEXT: .zero 3 ; Line table subsection for y -; X86-NEXT: .long 242 -; X86-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X86-NEXT: [[F2_START]]: -; X86-NEXT: .secrel32 _y -; X86-NEXT: .secidx _y -; X86-NEXT: .short 1 -; X86-NEXT: .long [[END_OF_Y]]-_y -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 2 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[Y_CALL]]-_y -; X86-NEXT: .long -2147483640 -; X86-NEXT: .long [[Y_RETURN]]-_y -; X86-NEXT: .long -2147483639 -; X86-NEXT: .short 52 -; X86-NEXT: .short 0 -; X86-NEXT: .short 53 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; X86-NEXT: [[F2_END]]: +; X86: .cv_linetable 1, _y, [[END_OF_Y]] ; Symbol subsection for f ; X86-NEXT: .long 241 ; X86-NEXT: .long [[F1_END:.*]]-[[F1_START:.*]] @@ -160,51 +121,13 @@ ; Padding ; X86-NEXT: .zero 3 ; Line table subsection for f -; X86-NEXT: .long 242 -; X86-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X86-NEXT: [[F2_START]]: -; X86-NEXT: .secrel32 _f -; X86-NEXT: .secidx _f -; X86-NEXT: .short 1 -; X86-NEXT: .long [[END_OF_F]]-_f -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 4 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[F_CALLS_X]]-_f -; X86-NEXT: .long -2147483636 -; X86-NEXT: .long [[F_CALLS_Y]]-_f -; X86-NEXT: .long -2147483635 -; X86-NEXT: .long [[F_CALLS_Z]]-_f -; X86-NEXT: .long -2147483634 -; X86-NEXT: .long [[F_RETURN]]-_f -; X86-NEXT: .long -2147483633 -; X86-NEXT: .short 62 -; X86-NEXT: .short 0 -; X86-NEXT: .short 63 -; X86-NEXT: .short 0 -; X86-NEXT: .short 72 -; X86-NEXT: .short 0 -; X86-NEXT: .short 73 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; X86-NEXT: [[F2_END]]: -; File index to string table offset subsection -; X86-NEXT: .long 244 -; X86-NEXT: .long 8 -; X86-NEXT: .long 1 -; X86-NEXT: .long 0 -; String table -; X86-NEXT: .long 243 -; X86-NEXT: .long 13 -; X86-NEXT: .byte 0 -; X86-NEXT: .ascii "D:\\source.c" -; X86-NEXT: .byte 0 -; X86-NEXT: .zero 3 +; X86: .cv_linetable 2, _f, [[END_OF_F]] +; X86: .cv_filechecksums +; X86: .cv_stringtable ; OBJ32: Section { ; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ32: Characteristics [ (0x42100040) +; OBJ32: Characteristics [ (0x42300040) ; OBJ32: ] ; OBJ32: Relocations [ ; OBJ32-NEXT: 0x2C IMAGE_REL_I386_SECREL _x @@ -343,40 +266,41 @@ ; X64-LABEL: x: ; X64-NEXT: .L{{.*}}: -; X64-NEXT: [[X_START:.*]]:{{$}} +; X64: .cv_file 1 "D:\\source.c" +; X64: .cv_loc 0 1 3 0 is_stmt 0 # source.c:3:0 ; X64: # BB ; X64: subq $40, %rsp -; X64-NEXT: [[X_CALL_LINE:.*]]:{{$}} +; X64: .cv_loc 0 1 4 42 # source.c:4:42 ; X64-NEXT: callq z -; X64-NEXT: [[X_EPILOG_AND_RET:.*]]: +; X64: .cv_loc 0 1 5 43 # source.c:5:43 ; X64: addq $40, %rsp ; X64-NEXT: ret ; X64-NEXT: [[END_OF_X:.*]]: ; ; X64-LABEL: y: ; X64-NEXT: .L{{.*}}: -; X64-NEXT: [[Y_START:.*]]:{{$}} +; X64: .cv_loc 1 1 7 0 # source.c:7:0 ; X64: # BB ; X64: subq $40, %rsp -; X64-NEXT: [[Y_CALL_LINE:.*]]:{{$}} +; X64: .cv_loc 1 1 8 52 # source.c:8:52 ; X64-NEXT: callq z -; X64-NEXT: [[Y_EPILOG_AND_RET:.*]]: +; X64: .cv_loc 1 1 9 53 # source.c:9:53 ; X64: addq $40, %rsp ; X64-NEXT: ret ; X64-NEXT: [[END_OF_Y:.*]]: ; ; X64-LABEL: f: ; X64-NEXT: .L{{.*}}: -; X64-NEXT: [[F_START:.*]]:{{$}} +; X64: .cv_loc 2 1 11 0 # source.c:11:0 ; X64: # BB ; X64: subq $40, %rsp -; X64-NEXT: [[F_CALLS_X:.*]]:{{$}} +; X64: .cv_loc 2 1 12 62 # source.c:12:62 ; X64-NEXT: callq x -; X64-NEXT: [[F_CALLS_Y:.*]]: +; X64: .cv_loc 2 1 13 63 # source.c:13:63 ; X64: callq y -; X64-NEXT: [[F_CALLS_Z:.*]]: +; X64: .cv_loc 2 1 14 72 # source.c:14:72 ; X64: callq z -; X64-NEXT: [[F_EPILOG_AND_RET:.*]]: +; X64: .cv_loc 2 1 15 73 # source.c:15:73 ; X64: addq $40, %rsp ; X64-NEXT: ret ; X64-NEXT: [[END_OF_F:.*]]: @@ -405,31 +329,7 @@ ; Padding ; X64-NEXT: .zero 3 ; Line table subsection for x -; X64-NEXT: .long 242 -; X64-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X64-NEXT: [[F2_START]]: -; X64-NEXT: .secrel32 x -; X64-NEXT: .secidx x -; X64-NEXT: .short 1 -; X64-NEXT: .long [[END_OF_X]]-x -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 0 -; X64-NEXT: .long 3 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[X_START]]-x -; X64-NEXT: .long -2147483645 -; X64-NEXT: .long [[X_CALL_LINE]]-x -; X64-NEXT: .long -2147483644 -; X64-NEXT: .long [[X_EPILOG_AND_RET]]-x -; X64-NEXT: .long -2147483643 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 42 -; X64-NEXT: .short 0 -; X64-NEXT: .short 43 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; X64-NEXT: [[F2_END]]: +; X64: .cv_linetable 0, x, [[END_OF_X]] ; Symbol subsection for y ; X64-NEXT: .long 241 ; X64-NEXT: .long [[F1_END:.*]]-[[F1_START:.*]] @@ -452,31 +352,7 @@ ; Padding ; X64-NEXT: .zero 3 ; Line table subsection for y -; X64-NEXT: .long 242 -; X64-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X64-NEXT: [[F2_START]]: -; X64-NEXT: .secrel32 y -; X64-NEXT: .secidx y -; X64-NEXT: .short 1 -; X64-NEXT: .long [[END_OF_Y]]-y -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 0 -; X64-NEXT: .long 3 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[Y_START]]-y -; X64-NEXT: .long -2147483641 -; X64-NEXT: .long [[Y_CALL_LINE]]-y -; X64-NEXT: .long -2147483640 -; X64-NEXT: .long [[Y_EPILOG_AND_RET]]-y -; X64-NEXT: .long -2147483639 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 52 -; X64-NEXT: .short 0 -; X64-NEXT: .short 53 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; X64-NEXT: [[F2_END]]: +; X64: .cv_linetable 1, y, [[END_OF_Y]] ; Symbol subsection for f ; X64-NEXT: .long 241 ; X64-NEXT: .long [[F1_END:.*]]-[[F1_START:.*]] @@ -499,55 +375,15 @@ ; Padding ; X64-NEXT: .zero 3 ; Line table subsection for f -; X64-NEXT: .long 242 -; X64-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X64-NEXT: [[F2_START]]: -; X64-NEXT: .secrel32 f -; X64-NEXT: .secidx f -; X64-NEXT: .short 1 -; X64-NEXT: .long [[END_OF_F]]-f -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 0 -; X64-NEXT: .long 5 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[F_START]]-f -; X64-NEXT: .long -2147483637 -; X64-NEXT: .long [[F_CALLS_X]]-f -; X64-NEXT: .long -2147483636 -; X64-NEXT: .long [[F_CALLS_Y]]-f -; X64-NEXT: .long -2147483635 -; X64-NEXT: .long [[F_CALLS_Z]]-f -; X64-NEXT: .long -2147483634 -; X64-NEXT: .long [[F_EPILOG_AND_RET]]-f -; X64-NEXT: .long -2147483633 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 62 -; X64-NEXT: .short 0 -; X64-NEXT: .short 63 -; X64-NEXT: .short 0 -; X64-NEXT: .short 72 -; X64-NEXT: .short 0 -; X64-NEXT: .short 73 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; X64-NEXT: [[F2_END]]: +; X64: .cv_linetable 2, f, [[END_OF_F]] ; File index to string table offset subsection -; X64-NEXT: .long 244 -; X64-NEXT: .long 8 -; X64-NEXT: .long 1 -; X64-NEXT: .long 0 +; X64: .cv_filechecksums ; String table -; X64-NEXT: .long 243 -; X64-NEXT: .long 13 -; X64-NEXT: .byte 0 -; X64-NEXT: .ascii "D:\\source.c" -; X64-NEXT: .byte 0 -; X64-NEXT: .zero 3 +; X64: .cv_stringtable ; OBJ64: Section { ; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ64: Characteristics [ (0x42100040) +; OBJ64: Characteristics [ (0x42300040) ; OBJ64: ] ; OBJ64: Relocations [ ; OBJ64-NEXT: 0x2C IMAGE_REL_AMD64_SECREL x diff --git a/test/DebugInfo/COFF/simple.ll b/test/DebugInfo/COFF/simple.ll index ee154359cf8..7d31cca19e7 100644 --- a/test/DebugInfo/COFF/simple.ll +++ b/test/DebugInfo/COFF/simple.ll @@ -13,9 +13,10 @@ ; X86-LABEL: _f: ; X86: # BB -; X86-NEXT: [[CALL_LINE:^L.*]]:{{$}} +; X86: .cv_file 1 "D:\\test.c" +; X86: .cv_loc 0 1 4 2 is_stmt 0 # test.c:4:2 ; X86: calll _g -; X86-NEXT: [[RETURN_STMT:.*]]: +; X86: .cv_loc 0 1 5 0 # test.c:5:0 ; X86: ret ; X86-NEXT: [[END_OF_F:.*]]: ; @@ -43,44 +44,15 @@ ; Padding ; X86-NEXT: .zero 3 ; Line table -; X86-NEXT: .long 242 -; X86-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X86-NEXT: [[F2_START]]: -; X86-NEXT: .secrel32 _f -; X86-NEXT: .secidx _f -; X86-NEXT: .short 1 -; X86-NEXT: .long [[END_OF_F]]-_f -; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X86-NEXT: .long 0 -; X86-NEXT: .long 2 -; X86-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X86-NEXT: .long [[CALL_LINE]]-_f -; X86-NEXT: .long -2147483644 -; X86-NEXT: .long [[RETURN_STMT]]-_f -; X86-NEXT: .long -2147483643 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: .short 0 -; X86-NEXT: [[FILE_SEGMENT_END]]: -; X86-NEXT: [[F2_END]]: +; X86-NEXT: .cv_linetable 0, _f, [[END_OF_F]] ; File index to string table offset subsection -; X86-NEXT: .long 244 -; X86-NEXT: .long 8 -; X86-NEXT: .long 1 -; X86-NEXT: .long 0 +; X86-NEXT: .cv_filechecksums ; String table -; X86-NEXT: .long 243 -; X86-NEXT: .long 11 -; X86-NEXT: .byte 0 -; X86-NEXT: .ascii "D:\\test.c" -; X86-NEXT: .byte 0 -; Padding -; X86-NEXT: .zero 1 +; X86-NEXT: .cv_stringtable ; OBJ32: Section { ; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ32: Characteristics [ (0x42100040) +; OBJ32: Characteristics [ (0x42300040) ; OBJ32: ] ; OBJ32: Relocations [ ; OBJ32-NEXT: 0x2C IMAGE_REL_I386_SECREL _f @@ -108,7 +80,7 @@ ; OBJ32-NEXT: LineNumberStart: 4 ; OBJ32-NEXT: LineNumberEndDelta: 0 ; OBJ32-NEXT: IsStatement: Yes -; OBJ32-NEXT: ColStart: 0 +; OBJ32-NEXT: ColStart: 2 ; OBJ32-NEXT: ColEnd: 0 ; OBJ32-NEXT: ] ; OBJ32-NEXT: +0x5 [ @@ -123,12 +95,13 @@ ; X64-LABEL: f: ; X64-NEXT: .L{{.*}}:{{$}} -; X64-NEXT: [[START:.*]]:{{$}} +; X64: .cv_file 1 "D:\\test.c" +; X64: .cv_loc 0 1 3 0 is_stmt 0 # test.c:3:0 ; X64: # BB ; X64: subq $40, %rsp -; X64-NEXT: [[CALL_LINE:.*]]:{{$}} +; X64: .cv_loc 0 1 4 2 # test.c:4:2 ; X64-NEXT: callq g -; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64: .cv_loc 0 1 5 0 # test.c:5:0 ; X64: addq $40, %rsp ; X64-NEXT: ret ; X64-NEXT: [[END_OF_F:.*]]: @@ -157,48 +130,15 @@ ; Padding ; X64-NEXT: .zero 3 ; Line table -; X64-NEXT: .long 242 -; X64-NEXT: .long [[F2_END:.*]]-[[F2_START:.*]] -; X64-NEXT: [[F2_START]]: -; X64-NEXT: .secrel32 f -; X64-NEXT: .secidx f -; X64-NEXT: .short 1 -; X64-NEXT: .long [[END_OF_F]]-f -; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: -; X64-NEXT: .long 0 -; X64-NEXT: .long 3 -; X64-NEXT: .long [[FILE_SEGMENT_END:.*]]-[[FILE_SEGMENT_START]] -; X64-NEXT: .long [[START]]-f -; X64-NEXT: .long -2147483645 -; X64-NEXT: .long [[CALL_LINE]]-f -; X64-NEXT: .long -2147483644 -; X64-NEXT: .long [[EPILOG_AND_RET]]-f -; X64-NEXT: .long -2147483643 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: .short 0 -; X64-NEXT: [[FILE_SEGMENT_END]]: -; X64-NEXT: [[F2_END]]: +; X64-NEXT: .cv_linetable 0, f, [[END_OF_F]] ; File index to string table offset subsection -; X64-NEXT: .long 244 -; X64-NEXT: .long 8 -; X64-NEXT: .long 1 -; X64-NEXT: .long 0 +; X64-NEXT: .cv_filechecksums ; String table -; X64-NEXT: .long 243 -; X64-NEXT: .long 11 -; X64-NEXT: .byte 0 -; X64-NEXT: .ascii "D:\\test.c" -; X64-NEXT: .byte 0 -; Padding -; X64-NEXT: .zero 1 +; X64-NEXT: .cv_stringtable ; OBJ64: Section { ; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) -; OBJ64: Characteristics [ (0x42100040) +; OBJ64: Characteristics [ (0x42300040) ; OBJ64: ] ; OBJ64: Relocations [ ; OBJ64-NEXT: 0x2C IMAGE_REL_AMD64_SECREL f @@ -233,7 +173,7 @@ ; OBJ64-NEXT: LineNumberStart: 4 ; OBJ64-NEXT: LineNumberEndDelta: 0 ; OBJ64-NEXT: IsStatement: Yes -; OBJ64-NEXT: ColStart: 0 +; OBJ64-NEXT: ColStart: 2 ; OBJ64-NEXT: ColEnd: 0 ; OBJ64-NEXT: ] ; OBJ64-NEXT: +0x9 [ @@ -274,5 +214,5 @@ attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" " !9 = !{i32 2, !"CodeView", i32 1} !10 = !{i32 1, !"Debug Info Version", i32 3} !11 = !{!"clang version 3.5 "} -!12 = !DILocation(line: 4, scope: !4) +!12 = !DILocation(line: 4, column: 2, scope: !4) !13 = !DILocation(line: 5, scope: !4) diff --git a/test/DebugInfo/COFF/tail-call-without-lexical-scopes.ll b/test/DebugInfo/COFF/tail-call-without-lexical-scopes.ll index a21c8bb0d97..a231eccacc0 100644 --- a/test/DebugInfo/COFF/tail-call-without-lexical-scopes.ll +++ b/test/DebugInfo/COFF/tail-call-without-lexical-scopes.ll @@ -15,10 +15,10 @@ ; The bar function happens to have no lexical scopes, yet it has one instruction ; with debug information available. This used to be PR19239. +; X86: .cv_file 1 "D:\\test.cpp" + ; X86-LABEL: {{^}}"?bar@@YAXHZZ": -; X86-NEXT: L{{.*}}: -; X86-NEXT: # BB -; X86-NEXT: [[JMP_LINE:^L.*]]:{{$}} +; X86: .cv_loc 1 1 4 0 ; X86: jmp "?foo@@YAXXZ" ; X86-NEXT: [[END_OF_BAR:^L.*]]:{{$}} ; X86-NOT: ret @@ -26,13 +26,9 @@ ; X86-LABEL: .section .debug$S,"dr" ; X86: .secrel32 "?bar@@YAXHZZ" ; X86-NEXT: .secidx "?bar@@YAXHZZ" -; X86: .long 0 -; X86-NEXT: .long 1 -; X86-NEXT: .long {{.*}} -; X86-NEXT: .long [[JMP_LINE]]-"?bar@@YAXHZZ" -; X86-NEXT: .long -2147483644 - -; X86-LABEL: .long 244 +; X86: .cv_linetable 1, "?bar@@YAXHZZ", [[END_OF_BAR]] +; X86: .cv_filechecksums +; X86: .cv_stringtable ; ModuleID = 'test.cpp' target datalayout = "e-m:w-p:32:32-i64:64-f80:32-n8:16:32-S32" diff --git a/test/MC/COFF/cv-loc.s b/test/MC/COFF/cv-loc.s new file mode 100644 index 00000000000..7cd845ebb37 --- /dev/null +++ b/test/MC/COFF/cv-loc.s @@ -0,0 +1,85 @@ +# RUN: llvm-mc < %s -triple=x86_64-pc-win32 -filetype=obj | llvm-readobj - -codeview | FileCheck %s + +.section .debug$S +.long 4 +.cv_stringtable + +.cv_file 1 "a.c" +.cv_file 2 "t.inc" + +# Implements this C: +# void f(volatile int *x) { +# ++*x; +# #include "t.h" // contains two ++*x; statements +# ++*x; +# } + +.text +.def f; + .scl 2; + .type 32; + .endef + .text + .globl f + .align 16, 0x90 +f: +.Lfunc_begin0: + .cv_loc 0 1 5 2 + incl (%rdi) + # #include "t.h" start + .cv_loc 0 2 0 0 + incl (%rdi) + .cv_loc 0 2 1 0 + incl (%rdi) + # #include "t.h" end + .cv_loc 0 1 6 2 + incl (%rdi) + retq +.Lfunc_end0: + +.section .debug$S +.cv_filechecksums +.cv_linetable 0, f, .Lfunc_end0 + +# CHECK: FunctionLineTable [ +# CHECK: LinkageName: f +# CHECK: Flags: 0x1 +# CHECK: CodeSize: 0x9 +# CHECK: FilenameSegment [ +# CHECK: Filename: a.c (0x0) +# CHECK: +0x0 [ +# CHECK: LineNumberStart: 5 +# CHECK: LineNumberEndDelta: 0 +# CHECK: IsStatement: Yes +# CHECK: ColStart: 2 +# CHECK: ColEnd: 0 +# CHECK: ] +# CHECK: ] +# CHECK: FilenameSegment [ +# CHECK: Filename: t.inc (0x8) +# CHECK: +0x2 [ +# CHECK: LineNumberStart: 0 +# CHECK: LineNumberEndDelta: 0 +# CHECK: IsStatement: Yes +# CHECK: ColStart: 0 +# CHECK: ColEnd: 0 +# CHECK: ] +# CHECK: +0x4 [ +# CHECK: LineNumberStart: 1 +# CHECK: LineNumberEndDelta: 0 +# CHECK: IsStatement: Yes +# CHECK: ColStart: 0 +# CHECK: ColEnd: 0 +# CHECK: ] +# CHECK: ] +# CHECK: FilenameSegment [ +# CHECK: Filename: a.c (0x0) +# CHECK: +0x6 [ +# CHECK: LineNumberStart: 6 +# CHECK: LineNumberEndDelta: 0 +# CHECK: IsStatement: Yes +# CHECK: ColStart: 2 +# CHECK: ColEnd: 0 +# CHECK: ] +# CHECK: ] +# CHECK: ] diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 0ac882866a7..a2f3e917e20 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -1116,8 +1116,7 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, uint32_t Offset = 6; // Skip relocations. uint16_t Flags = DE.getU16(&Offset); W.printHex("Flags", Flags); - bool HasColumnInformation = - Flags & COFF::DEBUG_LINE_TABLES_HAVE_COLUMN_RECORDS; + bool HasColumnInformation = Flags & codeview::LineFlags::HaveColumns; uint32_t FunctionSize = DE.getU32(&Offset); W.printHex("CodeSize", FunctionSize); while (DE.isValidOffset(Offset)) { @@ -1151,11 +1150,11 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, char Buffer[32]; format("+0x%X", PC).snprint(Buffer, 32); ListScope PCScope(W, Buffer); - uint32_t LineNumberStart = LineData & COFF::CVL_MaxLineNumber; + uint32_t LineNumberStart = LineData & codeview::LineInfo::StartLineMask; uint32_t LineNumberEndDelta = - (LineData >> COFF::CVL_LineNumberStartBits) & - COFF::CVL_LineNumberEndDeltaMask; - bool IsStatement = LineData & COFF::CVL_IsStatement; + (LineData & codeview::LineInfo::EndLineDeltaMask) >> + codeview::LineInfo::EndLineDeltaShift; + bool IsStatement = codeview::LineInfo::StatementFlag; W.printNumber("LineNumberStart", LineNumberStart); W.printNumber("LineNumberEndDelta", LineNumberEndDelta); W.printBoolean("IsStatement", IsStatement); diff --git a/unittests/MC/StringTableBuilderTest.cpp b/unittests/MC/StringTableBuilderTest.cpp index 4cc0bda0a03..f78d3588fff 100644 --- a/unittests/MC/StringTableBuilderTest.cpp +++ b/unittests/MC/StringTableBuilderTest.cpp @@ -68,4 +68,27 @@ TEST(StringTableBuilderTest, BasicWinCOFF) { EXPECT_EQ(23U, B.getOffset("river horse")); } +TEST(StringTableBuilderTest, ELFInOrder) { + StringTableBuilder B(StringTableBuilder::ELF); + EXPECT_EQ(1U, B.add("foo")); + EXPECT_EQ(5U, B.add("bar")); + EXPECT_EQ(9U, B.add("foobar")); + + B.finalizeInOrder(); + + std::string Expected; + Expected += '\x00'; + Expected += "foo"; + Expected += '\x00'; + Expected += "bar"; + Expected += '\x00'; + Expected += "foobar"; + Expected += '\x00'; + + EXPECT_EQ(Expected, B.data()); + EXPECT_EQ(1U, B.getOffset("foo")); + EXPECT_EQ(5U, B.getOffset("bar")); + EXPECT_EQ(9U, B.getOffset("foobar")); +} + }