diff options
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 184 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h | 70 | ||||
-rw-r--r-- | llvm/lib/MC/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/lib/MC/MCAsmStreamer.cpp | 72 | ||||
-rw-r--r-- | llvm/lib/MC/MCCodeView.cpp | 222 | ||||
-rw-r--r-- | llvm/lib/MC/MCContext.cpp | 17 | ||||
-rw-r--r-- | llvm/lib/MC/MCObjectStreamer.cpp | 31 | ||||
-rw-r--r-- | llvm/lib/MC/MCParser/AsmParser.cpp | 174 | ||||
-rw-r--r-- | llvm/lib/MC/MCStreamer.cpp | 16 | ||||
-rw-r--r-- | llvm/lib/MC/StringTableBuilder.cpp | 55 |
10 files changed, 640 insertions, 202 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index f1716361a1d..b503f1dc31e 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/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<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); + // 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/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h index 4294b4fd694..77b5ed39c1f 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/llvm/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<MCSymbol *, 10> Instrs; - MCSymbol *End; - FunctionInfo() : End(nullptr) {} + MCSymbol *End = nullptr; + unsigned FuncId = 0; + unsigned LastFileId; + bool HaveLineInfo = false; }; FunctionInfo *CurFn; - typedef DenseMap<const Function *, FunctionInfo> FnDebugInfoTy; - FnDebugInfoTy FnDebugInfo; - // Store the functions we've visited in a vector so we can maintain a stable - // order while emitting subsections. - SmallVector<const Function *, 10> VisitedFunctions; - - DenseMap<MCSymbol *, DebugLoc> LabelsAndLocs; - - // 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<StringRef, 10> Filenames; - struct PerFileInfo { - size_t FilenameID, StartOffset; - }; - StringMap<PerFileInfo> 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<PerFileInfo>::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; + unsigned NextFuncId = 0; + + /// Remember some debug info about each function. Keep it in a stable order to + /// emit at the end of the TU. + MapVector<const Function *, FunctionInfo> FnDebugInfo; + + /// Map from DIFile to .cv_file id. + DenseMap<const DIFile *, unsigned> FileIdMap; typedef std::map<const DIFile *, std::string> 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/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt index d1aac8104a3..a04f16c6ea7 100644 --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/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/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 2de36f8561c..d9382c5df9f 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/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/llvm/lib/MC/MCCodeView.cpp b/llvm/lib/MC/MCCodeView.cpp new file mode 100644 index 00000000000..2ccd7c6abae --- /dev/null +++ b/llvm/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 = "<stdin>"; + + 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<char> &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<MCCVLineEntry> 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/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp index b5ad518d033..c5243ef62c9 100644 --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/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/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index 39755e5b0a8..71d578aca9d 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/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/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index b1814f8c8ff..360de5db883 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/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<MCConstantExpr>(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/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 836b4054464..adae5d73ce5 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/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/llvm/lib/MC/StringTableBuilder.cpp b/llvm/lib/MC/StringTableBuilder.cpp index 80e552287b3..f50e098e6e4 100644 --- a/llvm/lib/MC/StringTableBuilder.cpp +++ b/llvm/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<StringRef, size_t> StringPair; @@ -62,13 +77,32 @@ tailcall: } void StringTableBuilder::finalize() { - std::vector<std::pair<StringRef, size_t> *> Strings; + finalizeStringTable(/*Optimize=*/true); +} + +void StringTableBuilder::finalizeInOrder() { + finalizeStringTable(/*Optimize=*/false); +} + +void StringTableBuilder::finalizeStringTable(bool Optimize) { + typedef std::pair<StringRef, size_t> StringOffsetPair; + std::vector<StringOffsetPair *> Strings; Strings.reserve(StringIndexMap.size()); - for (std::pair<StringRef, size_t> &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<StringRef, size_t> *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'; |