diff options
Diffstat (limited to 'llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp')
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 184 |
1 files changed, 142 insertions, 42 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index b503f1dc31e..f1716361a1d 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -13,7 +13,6 @@ #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" @@ -75,18 +74,6 @@ 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. @@ -98,26 +85,15 @@ void CodeViewDebug::maybeRecordLocation(DebugLoc DL, return; // Skip this line if it is longer than the maximum we can record. - LineInfo LI(DL.getLine(), DL.getLine(), /*IsStatement=*/true); - if (LI.getStartLine() != DL.getLine() || LI.isAlwaysStepInto() || - LI.isNeverStepInto()) + if (DL.getLine() > COFF::CVL_MaxLineNumber) return; - ColumnInfo CI(DL.getCol(), /*EndColumn=*/0); - if (CI.getStartColumn() != DL.getCol()) - return; - - 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()); + + MCSymbol *MCL = Asm->MMI->getContext().createTempSymbol(); + Asm->OutStreamer->EmitLabel(MCL); + CurFn->Instrs.push_back(MCL); + LabelsAndLocs[MCL] = DL; } CodeViewDebug::CodeViewDebug(AsmPrinter *AP) @@ -152,17 +128,39 @@ void CodeViewDebug::endModule() { // of the payload followed by the payload itself. The subsections are 4-byte // aligned. - // Emit per-function debug information. - for (auto &P : FnDebugInfo) - emitDebugInfoForFunction(P.first, P.second); + // 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]); // This subsection holds a file index to offset in string table table. Asm->OutStreamer->AddComment("File index to string table offset subsection"); - Asm->OutStreamer->EmitCVFileChecksumsDirective(); + 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); + } // This subsection holds the string table. Asm->OutStreamer->AddComment("String table"); - Asm->OutStreamer->EmitCVStringTableDirective(); + 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); clear(); } @@ -179,13 +177,21 @@ static void EmitLabelDiff(MCStreamer &Streamer, Streamer.EmitValue(AddrDelta, Size); } -void CodeViewDebug::emitDebugInfoForFunction(const Function *GV, - FunctionInfo &FI) { +static const DIFile *getFileFromLoc(DebugLoc DL) { + return DL.get()->getScope()->getFile(); +} + +void CodeViewDebug::emitDebugInfoForFunction(const Function *GV) { // 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(); @@ -232,8 +238,102 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV, // Every subsection must be aligned to a 4-byte boundary. Asm->OutStreamer->EmitFill((-FuncName.size()) % 4, 0); - // We have an assembler directive that takes care of the whole line table. - Asm->OutStreamer->EmitCVLinetableDirective(FI.FuncId, Fn, FI.End); + // 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<size_t, size_t> 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); } void CodeViewDebug::beginFunction(const MachineFunction *MF) { @@ -244,8 +344,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 @@ -284,9 +384,9 @@ void CodeViewDebug::endFunction(const MachineFunction *MF) { assert(FnDebugInfo.count(GV)); assert(CurFn == &FnDebugInfo[GV]); - // Don't emit anything if we don't have any line tables. - if (!CurFn->HaveLineInfo) { + if (CurFn->Instrs.empty()) { FnDebugInfo.erase(GV); + VisitedFunctions.pop_back(); } else { CurFn->End = Asm->getFunctionEnd(); } |