From 5c900cb299b6b8eedea45d331df140e4e5a5d838 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Fri, 29 Jan 2016 19:24:12 +0000 Subject: [PATCH] [CodeView] Implement .cv_inline_linetable This support is _very_ rudimentary, just enough to get some basic data into the CodeView debug section. Left to do is: - Use the combined opcodes to save space. - Do something about code offsets. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@259230 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/MC/MCCodeView.h | 34 ++++++++-- include/llvm/MC/MCObjectStreamer.h | 3 + include/llvm/MC/MCStreamer.h | 7 +++ lib/MC/MCAsmStreamer.cpp | 18 ++++++ lib/MC/MCCodeView.cpp | 74 +++++++++++++++++++++- lib/MC/MCObjectStreamer.cpp | 10 +++ lib/MC/MCParser/AsmParser.cpp | 55 +++++++++++++++- lib/MC/MCStreamer.cpp | 4 ++ test/MC/COFF/cv-inline-linetable.s | 124 +++++++++++++++++++++++++++++++++++++ 9 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 test/MC/COFF/cv-inline-linetable.s diff --git a/include/llvm/MC/MCCodeView.h b/include/llvm/MC/MCCodeView.h index ed2f35a8f94..d89e53bbf11 100644 --- a/include/llvm/MC/MCCodeView.h +++ b/include/llvm/MC/MCCodeView.h @@ -116,12 +116,24 @@ public: /// \brief Add a line entry. void addLineEntry(const MCCVLineEntry &LineEntry) { - MCCVLines[LineEntry.getFunctionId()].push_back(LineEntry); + size_t Offset = MCCVLines.size(); + auto I = + MCCVLineStartStop.insert({LineEntry.getFunctionId(), {Offset, Offset}}); + if (!I.second) + I.first->second.second = Offset; + MCCVLines.push_back(LineEntry); } - ArrayRef getFunctionLineEntries(unsigned FuncId) { - assert(MCCVLines.find(FuncId) != MCCVLines.end()); - return MCCVLines.find(FuncId)->second; + std::vector getFunctionLineEntries(unsigned FuncId) { + auto I = MCCVLineStartStop.find(FuncId); + assert(I != MCCVLineStartStop.end()); + + std::vector FilteredLines; + for (size_t Idx = I->second.first, End = I->second.second + 1; Idx != End; + ++Idx) + if (MCCVLines[Idx].getFunctionId() == FuncId) + FilteredLines.push_back(MCCVLines[Idx]); + return FilteredLines; } /// Emits a line table substream. @@ -129,6 +141,12 @@ public: const MCSymbol *FuncBegin, const MCSymbol *FuncEnd); + void emitInlineLineTableForFunction(MCObjectStreamer &OS, + unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds); + /// Emits the string table substream. void emitStringTable(MCObjectStreamer &OS); @@ -154,8 +172,12 @@ private: /// An array of absolute paths. Eventually this may include the file checksum. SmallVector Filenames; - /// A collection of MCDwarfLineEntry for each section. - std::map> MCCVLines; + /// The offset of the first and last .cv_loc directive for a given function + /// id. + std::map> MCCVLineStartStop; + + /// A collection of MCCVLineEntry for each section. + std::vector MCCVLines; }; } // end namespace llvm diff --git a/include/llvm/MC/MCObjectStreamer.h b/include/llvm/MC/MCObjectStreamer.h index fe5575c5018..8eb60f4693f 100644 --- a/include/llvm/MC/MCObjectStreamer.h +++ b/include/llvm/MC/MCObjectStreamer.h @@ -127,6 +127,9 @@ public: StringRef FileName) override; void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, const MCSymbol *End) override; + void EmitCVInlineLinetableDirective( + unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds) override; void EmitCVStringTableDirective() override; void EmitCVFileChecksumsDirective() override; void EmitGPRel32Value(const MCExpr *Value) override; diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index 1f4b24644ca..9aa33747249 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -655,6 +655,13 @@ public: const MCSymbol *FnStart, const MCSymbol *FnEnd); + /// \brief This implements the CodeView '.cv_inline_linetable' assembler + /// directive. + virtual void + EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds); + /// \brief This implements the CodeView '.cv_stringtable' assembler directive. virtual void EmitCVStringTableDirective() {} diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index d9382c5df9f..b31fd2c34dc 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -205,6 +205,9 @@ public: StringRef FileName) override; void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, const MCSymbol *FnEnd) override; + void EmitCVInlineLinetableDirective( + unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds) override; void EmitCVStringTableDirective() override; void EmitCVFileChecksumsDirective() override; @@ -1016,6 +1019,21 @@ void MCAsmStreamer::EmitCVLinetableDirective(unsigned FunctionId, this->MCStreamer::EmitCVLinetableDirective(FunctionId, FnStart, FnEnd); } +void MCAsmStreamer::EmitCVInlineLinetableDirective( + unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds) { + OS << "\t.cv_inline_linetable\t" << PrimaryFunctionId << ' ' << SourceFileId + << ' ' << SourceLineNum; + if (!SecondaryFunctionIds.empty()) { + OS << " contains"; + for (unsigned SecondaryFunctionId : SecondaryFunctionIds) + OS << ' ' << SecondaryFunctionId; + } + EmitEOL(); + this->MCStreamer::EmitCVInlineLinetableDirective( + PrimaryFunctionId, SourceFileId, SourceLineNum, SecondaryFunctionIds); +} + void MCAsmStreamer::EmitCVStringTableDirective() { OS << "\t.cv_stringtable"; EmitEOL(); diff --git a/lib/MC/MCCodeView.cpp b/lib/MC/MCCodeView.cpp index 95cf7ccea55..a876470b826 100644 --- a/lib/MC/MCCodeView.cpp +++ b/lib/MC/MCCodeView.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectStreamer.h" #include "llvm/Support/COFF.h" @@ -151,11 +152,11 @@ void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS, OS.EmitCOFFSectionIndex(FuncBegin); // Actual line info. - ArrayRef Locs = getFunctionLineEntries(FuncId); + std::vector Locs = getFunctionLineEntries(FuncId); bool HaveColumns = any_of(Locs, [](const MCCVLineEntry &LineEntry) { return LineEntry.getColumn() != 0; }); - OS.EmitIntValue(HaveColumns ? int(codeview::LineFlags::HaveColumns) : 0, 2); + OS.EmitIntValue(HaveColumns ? int(LineFlags::HaveColumns) : 0, 2); OS.emitAbsoluteSymbolDiff(FuncEnd, FuncBegin, 4); for (auto I = Locs.begin(), E = Locs.end(); I != E;) { @@ -180,7 +181,7 @@ void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS, OS.emitAbsoluteSymbolDiff(J->getLabel(), FuncBegin, 4); unsigned LineData = J->getLine(); if (J->isStmt()) - LineData |= codeview::LineInfo::StatementFlag; + LineData |= LineInfo::StatementFlag; OS.EmitIntValue(LineData, 4); } if (HaveColumns) { @@ -194,6 +195,73 @@ void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS, OS.EmitLabel(LineEnd); } +static bool compressAnnotation(uint32_t Data, SmallVectorImpl &Buffer) { + if (isUInt<7>(Data)) { + Buffer.push_back(Data); + return true; + } + + if (isUInt<14>(Data)) { + Buffer.push_back((Data >> 8) | 0x80); + Buffer.push_back(Data & 0xff); + return true; + } + + if (isUInt<29>(Data)) { + Buffer.push_back((Data >> 24) | 0xC0); + Buffer.push_back((Data >> 16) & 0xff); + Buffer.push_back((Data >> 8) & 0xff); + Buffer.push_back(Data & 0xff); + return true; + } + + return false; +} + +static uint32_t encodeSignedNumber(uint32_t Data) { + if (Data >> 31) + return ((-Data) << 1) | 1; + return Data << 1; +} + +void CodeViewContext::emitInlineLineTableForFunction( + MCObjectStreamer &OS, unsigned PrimaryFunctionId, unsigned SourceFileId, + unsigned SourceLineNum, ArrayRef SecondaryFunctionIds) { + std::vector Locs = getFunctionLineEntries(PrimaryFunctionId); + std::vector> Annotations; + + const MCCVLineEntry *LastLoc = nullptr; + unsigned LastFileId = SourceFileId; + unsigned LastLineNum = SourceLineNum; + + for (const MCCVLineEntry &Loc : Locs) { + if (!LastLoc) { + // TODO ChangeCodeOffset + // TODO ChangeCodeLength + } + + if (Loc.getFileNum() != LastFileId) + Annotations.push_back({ChangeFile, Loc.getFileNum()}); + + if (Loc.getLine() != LastLineNum) + Annotations.push_back( + {ChangeLineOffset, encodeSignedNumber(Loc.getLine() - LastLineNum)}); + + LastLoc = &Loc; + LastFileId = Loc.getFileNum(); + LastLineNum = Loc.getLine(); + } + + SmallString<32> Buffer; + for (auto Annotation : Annotations) { + BinaryAnnotationsOpCode Opcode = Annotation.first; + uint32_t Operand = Annotation.second; + compressAnnotation(Opcode, Buffer); + compressAnnotation(Operand, Buffer); + } + OS.EmitBytes(Buffer); +} + // // 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 diff --git a/lib/MC/MCObjectStreamer.cpp b/lib/MC/MCObjectStreamer.cpp index 71d578aca9d..4f0b597b434 100644 --- a/lib/MC/MCObjectStreamer.cpp +++ b/lib/MC/MCObjectStreamer.cpp @@ -384,6 +384,16 @@ void MCObjectStreamer::EmitCVLinetableDirective(unsigned FunctionId, this->MCStreamer::EmitCVLinetableDirective(FunctionId, Begin, End); } +void MCObjectStreamer::EmitCVInlineLinetableDirective( + unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds) { + getContext().getCVContext().emitInlineLineTableForFunction( + *this, PrimaryFunctionId, SourceFileId, SourceLineNum, + SecondaryFunctionIds); + this->MCStreamer::EmitCVInlineLinetableDirective( + PrimaryFunctionId, SourceFileId, SourceLineNum, SecondaryFunctionIds); +} + void MCObjectStreamer::EmitCVStringTableDirective() { getContext().getCVContext().emitStringTable(*this); } diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index 360de5db883..621618a5666 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -357,8 +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_CV_FILE, DK_CV_LOC, DK_CV_LINETABLE, DK_CV_INLINE_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, @@ -396,10 +396,11 @@ private: bool parseDirectiveLoc(); bool parseDirectiveStabs(); - // ".cv_file", ".cv_loc", ".cv_linetable" + // ".cv_file", ".cv_loc", ".cv_linetable", "cv_inline_linetable" bool parseDirectiveCVFile(); bool parseDirectiveCVLoc(); bool parseDirectiveCVLinetable(); + bool parseDirectiveCVInlineLinetable(); bool parseDirectiveCVStringTable(); bool parseDirectiveCVFileChecksums(); @@ -1653,6 +1654,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info, return parseDirectiveCVLoc(); case DK_CV_LINETABLE: return parseDirectiveCVLinetable(); + case DK_CV_INLINE_LINETABLE: + return parseDirectiveCVInlineLinetable(); case DK_CV_STRINGTABLE: return parseDirectiveCVStringTable(); case DK_CV_FILECHECKSUMS: @@ -3225,6 +3228,51 @@ bool AsmParser::parseDirectiveCVLinetable() { return false; } +/// parseDirectiveCVInlineLinetable +/// ::= .cv_inline_linetable PrimaryFunctionId FileId LineNum +/// ("contains" SecondaryFunctionId+)? +bool AsmParser::parseDirectiveCVInlineLinetable() { + int64_t PrimaryFunctionId = getTok().getIntVal(); + if (PrimaryFunctionId < 0) + return TokError( + "function id less than zero in '.cv_inline_linetable' directive"); + Lex(); + + int64_t SourceFileId = getTok().getIntVal(); + if (SourceFileId <= 0) + return TokError( + "File id less than zero in '.cv_inline_linetable' directive"); + Lex(); + + int64_t SourceLineNum = getTok().getIntVal(); + if (SourceLineNum < 0) + return TokError( + "Line number less than zero in '.cv_inline_linetable' directive"); + Lex(); + + SmallVector SecondaryFunctionIds; + if (getLexer().is(AsmToken::Identifier)) { + if (getTok().getIdentifier() != "contains") + return TokError( + "unexpected identifier in '.cv_inline_linetable' directive"); + Lex(); + + while (getLexer().isNot(AsmToken::EndOfStatement)) { + int64_t SecondaryFunctionId = getTok().getIntVal(); + if (SecondaryFunctionId < 0) + return TokError( + "function id less than zero in '.cv_inline_linetable' directive"); + Lex(); + + SecondaryFunctionIds.push_back(SecondaryFunctionId); + } + } + + getStreamer().EmitCVInlineLinetableDirective( + PrimaryFunctionId, SourceFileId, SourceLineNum, SecondaryFunctionIds); + return false; +} + /// parseDirectiveCVStringTable /// ::= .cv_stringtable bool AsmParser::parseDirectiveCVStringTable() { @@ -4553,6 +4601,7 @@ void AsmParser::initializeDirectiveKindMap() { DirectiveKindMap[".cv_file"] = DK_CV_FILE; DirectiveKindMap[".cv_loc"] = DK_CV_LOC; DirectiveKindMap[".cv_linetable"] = DK_CV_LINETABLE; + DirectiveKindMap[".cv_inline_linetable"] = DK_CV_INLINE_LINETABLE; DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE; DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS; DirectiveKindMap[".sleb128"] = DK_SLEB128; diff --git a/lib/MC/MCStreamer.cpp b/lib/MC/MCStreamer.cpp index adae5d73ce5..1f445ca5893 100644 --- a/lib/MC/MCStreamer.cpp +++ b/lib/MC/MCStreamer.cpp @@ -196,6 +196,10 @@ void MCStreamer::EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, const MCSymbol *End) {} +void MCStreamer::EmitCVInlineLinetableDirective( + unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, + ArrayRef SecondaryFunctionIds) {} + void MCStreamer::EmitEHSymAttributes(const MCSymbol *Symbol, MCSymbol *EHSymbol) { } diff --git a/test/MC/COFF/cv-inline-linetable.s b/test/MC/COFF/cv-inline-linetable.s new file mode 100644 index 00000000000..db83de97ac8 --- /dev/null +++ b/test/MC/COFF/cv-inline-linetable.s @@ -0,0 +1,124 @@ +# RUN: llvm-mc -triple=i686-pc-win32 -filetype=obj < %s | llvm-readobj -codeview | FileCheck %s + .text + .def @feat.00; + .scl 3; + .type 0; + .endef + .globl @feat.00 +@feat.00 = 1 + .def "?baz@@YAXXZ"; + .scl 2; + .type 32; + .endef + .globl "?baz@@YAXXZ" + .p2align 4, 0x90 +"?baz@@YAXXZ": # @"\01?baz@@YAXXZ" +Lfunc_begin0: + .cv_file 1 "D:\\src\\llvm\\build\\t.cpp" + .cv_loc 0 1 13 0 is_stmt 0 # t.cpp:13:0 +# BB#0: # %entry + pushl %eax + .cv_loc 0 1 14 5 # t.cpp:14:5 + addl $6, "?x@@3HC" + .cv_loc 1 1 9 5 # t.cpp:9:5 + addl $4, "?x@@3HC" + .cv_loc 2 1 3 7 # t.cpp:3:7 + movl $1, (%esp) + leal (%esp), %eax + .cv_loc 2 1 4 5 # t.cpp:4:5 + addl %eax, "?x@@3HC" + .cv_loc 2 1 5 5 # t.cpp:5:5 + addl $2, "?x@@3HC" + .cv_loc 2 1 6 5 # t.cpp:6:5 + addl $3, "?x@@3HC" + .cv_loc 1 1 11 5 # t.cpp:11:5 + addl $5, "?x@@3HC" + .cv_loc 0 1 16 5 # t.cpp:16:5 + addl $7, "?x@@3HC" + .cv_loc 0 1 17 1 # t.cpp:17:1 + popl %eax + retl +Lfunc_end0: + + .section .debug$T,"dr" + .long 4 + .short 6 + .short 4609 + .long 0 + .short 14 + .short 4104 + .asciz "\003\000\000\000\000\000\000\000\000\020\000" + .short 14 + .short 5633 + .asciz "\000\000\000\000\001\020\000" + .ascii "baz" + .byte 0 + .short 14 + .short 5633 + .asciz "\000\000\000\000\001\020\000" + .ascii "bar" + .byte 0 + .short 14 + .short 5633 + .asciz "\000\000\000\000\001\020\000" + .ascii "foo" + .byte 0 + .section .debug$S,"dr" + .long 4 + .long 241 # Symbol subsection for baz + .long Ltmp1-Ltmp0 +Ltmp0: + .short Ltmp3-Ltmp2 +Ltmp2: + .short 4423 + .zero 12 + .long Lfunc_end0-"?baz@@YAXXZ" + .zero 12 + .secrel32 "?baz@@YAXXZ" + .secidx "?baz@@YAXXZ" + .byte 0 + .ascii "baz" + .byte 0 +Ltmp3: + .short Ltmp5-Ltmp4 +Ltmp4: + .short 4429 + .asciz "\000\000\000\000\000\000\000\000\003\020\000" + .cv_inline_linetable 1 1 9 contains 2 +# CHECK: InlineSite { +# CHECK: PtrParent: 0x0 +# CHECK: PtrEnd: 0x0 +# CHECK: Inlinee: bar (0x1003) +# CHECK: BinaryAnnotations [ +# CHECK: ChangeLineOffset: 2 +# CHECK: ] +# CHECK: } +Ltmp5: + .short Ltmp7-Ltmp6 +Ltmp6: + .short 4429 + .asciz "\000\000\000\000\000\000\000\000\004\020\000" + .cv_inline_linetable 2 1 3 +# CHECK: InlineSite { +# CHECK: PtrParent: 0x0 +# CHECK: PtrEnd: 0x0 +# CHECK: Inlinee: foo (0x1004) +# CHECK: BinaryAnnotations [ +# CHECK: ChangeLineOffset: 1 +# CHECK: ChangeLineOffset: 1 +# CHECK: ChangeLineOffset: 1 +# CHECK: ] +# CHECK: } +Ltmp7: + .short 2 + .short 4430 + .short 2 + .short 4430 + .short 2 + .short 4431 +Ltmp1: + .zero 1 + .cv_linetable 0, "?baz@@YAXXZ", Lfunc_end0 + .cv_filechecksums # File index to string table offset subsection + .cv_stringtable # String table + -- 2.11.0