OSDN Git Service

[CodeView] Implement .cv_inline_linetable
authorDavid Majnemer <david.majnemer@gmail.com>
Fri, 29 Jan 2016 19:24:12 +0000 (19:24 +0000)
committerDavid Majnemer <david.majnemer@gmail.com>
Fri, 29 Jan 2016 19:24:12 +0000 (19:24 +0000)
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
include/llvm/MC/MCObjectStreamer.h
include/llvm/MC/MCStreamer.h
lib/MC/MCAsmStreamer.cpp
lib/MC/MCCodeView.cpp
lib/MC/MCObjectStreamer.cpp
lib/MC/MCParser/AsmParser.cpp
lib/MC/MCStreamer.cpp
test/MC/COFF/cv-inline-linetable.s [new file with mode: 0644]

index ed2f35a..d89e53b 100644 (file)
@@ -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<MCCVLineEntry> getFunctionLineEntries(unsigned FuncId) {
-    assert(MCCVLines.find(FuncId) != MCCVLines.end());
-    return MCCVLines.find(FuncId)->second;
+  std::vector<MCCVLineEntry> getFunctionLineEntries(unsigned FuncId) {
+    auto I = MCCVLineStartStop.find(FuncId);
+    assert(I != MCCVLineStartStop.end());
+
+    std::vector<MCCVLineEntry> 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<unsigned> 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<StringRef, 4> Filenames;
 
-  /// A collection of MCDwarfLineEntry for each section.
-  std::map<int, std::vector<MCCVLineEntry>> MCCVLines;
+  /// The offset of the first and last .cv_loc directive for a given function
+  /// id.
+  std::map<int, std::pair<size_t, size_t>> MCCVLineStartStop;
+
+  /// A collection of MCCVLineEntry for each section.
+  std::vector<MCCVLineEntry> MCCVLines;
 };
 
 } // end namespace llvm
index fe5575c..8eb60f4 100644 (file)
@@ -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<unsigned> SecondaryFunctionIds) override;
   void EmitCVStringTableDirective() override;
   void EmitCVFileChecksumsDirective() override;
   void EmitGPRel32Value(const MCExpr *Value) override;
index 1f4b246..9aa3374 100644 (file)
@@ -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<unsigned> SecondaryFunctionIds);
+
   /// \brief This implements the CodeView '.cv_stringtable' assembler directive.
   virtual void EmitCVStringTableDirective() {}
 
index d9382c5..b31fd2c 100644 (file)
@@ -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<unsigned> 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<unsigned> 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();
index 95cf7cc..a876470 100644 (file)
@@ -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<MCCVLineEntry> Locs = getFunctionLineEntries(FuncId);
+  std::vector<MCCVLineEntry> 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<char> &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<unsigned> SecondaryFunctionIds) {
+  std::vector<MCCVLineEntry> Locs = getFunctionLineEntries(PrimaryFunctionId);
+  std::vector<std::pair<BinaryAnnotationsOpCode, uint32_t>> 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
index 71d578a..4f0b597 100644 (file)
@@ -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<unsigned> SecondaryFunctionIds) {
+  getContext().getCVContext().emitInlineLineTableForFunction(
+      *this, PrimaryFunctionId, SourceFileId, SourceLineNum,
+      SecondaryFunctionIds);
+  this->MCStreamer::EmitCVInlineLinetableDirective(
+      PrimaryFunctionId, SourceFileId, SourceLineNum, SecondaryFunctionIds);
+}
+
 void MCObjectStreamer::EmitCVStringTableDirective() {
   getContext().getCVContext().emitStringTable(*this);
 }
index 360de5d..621618a 100644 (file)
@@ -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<unsigned, 8> 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;
index adae5d7..1f445ca 100644 (file)
@@ -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<unsigned> 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 (file)
index 0000000..db83de9
--- /dev/null
@@ -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
+