[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.

llvm-svn: 259230
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index d9382c5..b31fd2c 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -205,6 +205,9 @@
                           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 @@
   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();
diff --git a/llvm/lib/MC/MCCodeView.cpp b/llvm/lib/MC/MCCodeView.cpp
index 95cf7cc..a876470 100644
--- a/llvm/lib/MC/MCCodeView.cpp
+++ b/llvm/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 @@
   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 @@
       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 @@
   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
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index 71d578a..4f0b597 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -384,6 +384,16 @@
   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);
 }
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 360de5d..621618a 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -357,8 +357,8 @@
     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 @@
   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 @@
       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 @@
   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 @@
   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/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp
index adae5d7..1f445ca 100644
--- a/llvm/lib/MC/MCStreamer.cpp
+++ b/llvm/lib/MC/MCStreamer.cpp
@@ -196,6 +196,10 @@
                                           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) {
 }