/// \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.
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);
/// 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
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;
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() {}
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;
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();
#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"
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;) {
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) {
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
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);
}
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,
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();
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:
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() {
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;
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) {
}
--- /dev/null
+# 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
+