From bdfdb248197dd795c34006fd0aff4a38719a1ef1 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 7 Sep 2016 16:15:31 +0000 Subject: [PATCH] [codeview] Add new directives to record inlined call site line info Summary: Previously we were trying to represent this with the "contains" list of the .cv_inline_linetable directive, which was not enough information. Now we directly represent the chain of inlined call sites, so we know what location to emit when we encounter a .cv_loc directive of an inner inlined call site while emitting the line table of an outer function or inlined call site. Fixes PR29146. Also fixes PR29147, where we would crash when .cv_loc directives crossed sections. Now we write down the section of the first .cv_loc directive, and emit an error if any other .cv_loc directive for that function is in a different section. Also fixes issues with discontiguous inlined source locations, like in this example: volatile int unlikely_cond = 0; extern void __declspec(noreturn) abort(); __forceinline void f() { if (!unlikely_cond) abort(); } int main() { unlikely_cond = 0; f(); unlikely_cond = 0; } Previously our tables gave bad location information for the 'abort' call, and the debugger wouldn't snow the inlined stack frame for 'f'. It is important to emit good line tables for this code pattern, because it comes up whenever an asan bug occurs in an inlined function. The __asan_report* stubs are generally placed after the normal function epilogue, leading to discontiguous regions of inlined code. Reviewers: majnemer, amccarth Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D24014 llvm-svn: 280822 --- include/llvm/MC/MCCodeView.h | 83 +++++++- include/llvm/MC/MCFragment.h | 5 +- include/llvm/MC/MCObjectStreamer.h | 11 +- include/llvm/MC/MCStreamer.h | 20 +- lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 30 ++- lib/MC/MCAsmStreamer.cpp | 56 +++-- lib/MC/MCCodeView.cpp | 152 +++++++++----- lib/MC/MCObjectStreamer.cpp | 12 +- lib/MC/MCParser/AsmParser.cpp | 169 ++++++++++++---- lib/MC/MCStreamer.cpp | 46 ++++- test/DebugInfo/COFF/inlining-header.ll | 7 +- test/DebugInfo/COFF/inlining-levels.ll | 4 +- test/DebugInfo/COFF/inlining.ll | 10 +- test/DebugInfo/COFF/local-variables.ll | 3 + test/MC/COFF/cv-def-range.s | 1 + test/MC/COFF/cv-empty-linetable.s | 1 + test/MC/COFF/cv-errors.s | 56 +++++ test/MC/COFF/cv-inline-linetable-infloop.s | 3 + test/MC/COFF/cv-inline-linetable-unlikely.s | 191 ++++++++++++++++++ .../MC/COFF/cv-inline-linetable-unreachable.s | 2 + test/MC/COFF/cv-inline-linetable.s | 27 ++- test/MC/COFF/cv-loc-cross-section.s | 26 +++ test/MC/COFF/cv-loc.s | 2 + 23 files changed, 741 insertions(+), 176 deletions(-) create mode 100644 test/MC/COFF/cv-errors.s create mode 100644 test/MC/COFF/cv-inline-linetable-unlikely.s create mode 100644 test/MC/COFF/cv-loc-cross-section.s diff --git a/include/llvm/MC/MCCodeView.h b/include/llvm/MC/MCCodeView.h index 9f881faa492..41521a6549b 100644 --- a/include/llvm/MC/MCCodeView.h +++ b/include/llvm/MC/MCCodeView.h @@ -105,6 +105,55 @@ public: static void Make(MCObjectStreamer *MCOS); }; +/// Information describing a function or inlined call site introduced by +/// .cv_func_id or .cv_inline_site_id. Accumulates information from .cv_loc +/// directives used with this function's id or the id of an inlined call site +/// within this function or inlined call site. +struct MCCVFunctionInfo { + /// If this represents an inlined call site, then ParentFuncIdPlusOne will be + /// the parent function id plus one. If this represents a normal function, + /// then there is no parent, and ParentFuncIdPlusOne will be FunctionSentinel. + /// If this struct is an unallocated slot in the function info vector, then + /// ParentFuncIdPlusOne will be zero. + unsigned ParentFuncIdPlusOne = 0; + + enum : unsigned { FunctionSentinel = ~0U }; + + struct LineInfo { + unsigned File; + unsigned Line; + unsigned Col; + }; + + LineInfo InlinedAt; + + /// The section of the first .cv_loc directive used for this function, or null + /// if none has been seen yet. + MCSection *Section = nullptr; + + /// Map from inlined call site id to the inlined at location to use for that + /// call site. Call chains are collapsed, so for the call chain 'f -> g -> h', + /// the InlinedAtMap of 'f' will contain entries for 'g' and 'h' that both + /// list the line info for the 'g' call site. + DenseMap InlinedAtMap; + + /// Returns true if this is function info has not yet been used in a + /// .cv_func_id or .cv_inline_site_id directive. + bool isUnallocatedFunctionInfo() const { return ParentFuncIdPlusOne == 0; } + + /// Returns true if this represents an inlined call site, meaning + /// ParentFuncIdPlusOne is neither zero nor ~0U. + bool isInlinedCallSite() const { + return !isUnallocatedFunctionInfo() && + ParentFuncIdPlusOne != FunctionSentinel; + } + + unsigned getParentFuncId() const { + assert(isInlinedCallSite()); + return ParentFuncIdPlusOne - 1; + } +}; + /// Holds state from .cv_file and .cv_loc directives for later emission. class CodeViewContext { public: @@ -115,6 +164,27 @@ public: bool addFile(unsigned FileNumber, StringRef Filename); ArrayRef getFilenames() { return Filenames; } + /// Records the function id of a normal function. Returns false if the + /// function id has already been used, and true otherwise. + bool recordFunctionId(unsigned FuncId); + + /// Records the function id of an inlined call site. Records the "inlined at" + /// location info of the call site, including what function or inlined call + /// site it was inlined into. Returns false if the function id has already + /// been used, and true otherwise. + bool recordInlinedCallSiteId(unsigned FuncId, unsigned IAFunc, + unsigned IAFile, unsigned IALine, + unsigned IACol); + + /// Retreive the function info if this is a valid function id, or nullptr. + MCCVFunctionInfo *getCVFunctionInfo(unsigned FuncId) { + if (FuncId >= Functions.size()) + return nullptr; + if (Functions[FuncId].isUnallocatedFunctionInfo()) + return nullptr; + return &Functions[FuncId]; + } + /// Saves the information from the currently parsed .cv_loc directive /// and sets CVLocSeen. When the next instruction is assembled an entry /// in the line number table with this information and the address of the @@ -179,10 +249,12 @@ public: const MCSymbol *FuncBegin, const MCSymbol *FuncEnd); - void emitInlineLineTableForFunction( - MCObjectStreamer &OS, unsigned PrimaryFunctionId, unsigned SourceFileId, - unsigned SourceLineNum, const MCSymbol *FnStartSym, - const MCSymbol *FnEndSym, ArrayRef SecondaryFunctionIds); + void emitInlineLineTableForFunction(MCObjectStreamer &OS, + unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym); /// Encodes the binary annotations once we have a layout. void encodeInlineLineTable(MCAsmLayout &Layout, @@ -230,6 +302,9 @@ private: /// A collection of MCCVLineEntry for each section. std::vector MCCVLines; + + /// All known functions and inlined call sites, indexed by function id. + std::vector Functions; }; } // end namespace llvm diff --git a/include/llvm/MC/MCFragment.h b/include/llvm/MC/MCFragment.h index 22e90e20aa9..02b73251c84 100644 --- a/include/llvm/MC/MCFragment.h +++ b/include/llvm/MC/MCFragment.h @@ -491,7 +491,6 @@ class MCCVInlineLineTableFragment : public MCFragment { unsigned StartLineNum; const MCSymbol *FnStartSym; const MCSymbol *FnEndSym; - SmallVector SecondaryFuncs; SmallString<8> Contents; /// CodeViewContext has the real knowledge about this format, so let it access @@ -502,12 +501,10 @@ public: MCCVInlineLineTableFragment(unsigned SiteFuncId, unsigned StartFileId, unsigned StartLineNum, const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFuncs, MCSection *Sec = nullptr) : MCFragment(FT_CVInlineLines, false, 0, Sec), SiteFuncId(SiteFuncId), StartFileId(StartFileId), StartLineNum(StartLineNum), - FnStartSym(FnStartSym), FnEndSym(FnEndSym), - SecondaryFuncs(SecondaryFuncs.begin(), SecondaryFuncs.end()) {} + FnStartSym(FnStartSym), FnEndSym(FnEndSym) {} /// \name Accessors /// @{ diff --git a/include/llvm/MC/MCObjectStreamer.h b/include/llvm/MC/MCObjectStreamer.h index 28c6a414973..0d5389988bb 100644 --- a/include/llvm/MC/MCObjectStreamer.h +++ b/include/llvm/MC/MCObjectStreamer.h @@ -124,13 +124,14 @@ public: const MCSymbol *Label); void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName) override; + StringRef FileName, SMLoc Loc) override; void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, const MCSymbol *End) override; - void EmitCVInlineLinetableDirective( - unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, - const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFunctionIds) override; + void EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym) override; void EmitCVDefRangeDirective( ArrayRef> Ranges, StringRef FixedSizePortion) override; diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index e5025400671..99d07ff2003 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -713,11 +713,20 @@ public: /// success. virtual bool EmitCVFileDirective(unsigned FileNo, StringRef Filename); + /// \brief Introduces a function id for use with .cv_loc. + virtual bool EmitCVFuncIdDirective(unsigned FunctionId); + + /// \brief Introduces an inline call site id for use with .cv_loc. Includes + /// extra information for inline line table generation. + virtual bool EmitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, + unsigned IAFile, unsigned IALine, + unsigned IACol, SMLoc Loc); + /// \brief This implements the CodeView '.cv_loc' assembler directive. virtual void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName); + StringRef FileName, SMLoc Loc); /// \brief This implements the CodeView '.cv_linetable' assembler directive. virtual void EmitCVLinetableDirective(unsigned FunctionId, @@ -726,10 +735,11 @@ public: /// \brief This implements the CodeView '.cv_inline_linetable' assembler /// directive. - virtual void EmitCVInlineLinetableDirective( - unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, - const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFunctionIds); + virtual void EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym); /// \brief This implements the CodeView '.cv_def_range' assembler /// directive. diff --git a/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index 63d9a4744a4..51a5d06b954 100644 --- a/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -124,7 +124,16 @@ CodeViewDebug::getInlineSite(const DILocation *InlinedAt, auto SiteInsertion = CurFn->InlineSites.insert({InlinedAt, InlineSite()}); InlineSite *Site = &SiteInsertion.first->second; if (SiteInsertion.second) { + unsigned ParentFuncId = CurFn->FuncId; + if (const DILocation *OuterIA = InlinedAt->getInlinedAt()) + ParentFuncId = + getInlineSite(OuterIA, InlinedAt->getScope()->getSubprogram()) + .SiteFuncId; + Site->SiteFuncId = NextFuncId++; + OS.EmitCVInlineSiteIdDirective( + Site->SiteFuncId, ParentFuncId, maybeRecordFile(InlinedAt->getFile()), + InlinedAt->getLine(), InlinedAt->getColumn(), SMLoc()); Site->Inlinee = Inlinee; InlinedSubprograms.insert(Inlinee); getFuncIdForSubprogram(Inlinee); @@ -357,8 +366,8 @@ void CodeViewDebug::maybeRecordLocation(const DebugLoc &DL, } OS.EmitCVLocDirective(FuncId, FileId, DL.getLine(), DL.getCol(), - /*PrologueEnd=*/false, - /*IsStmt=*/false, DL->getFilename()); + /*PrologueEnd=*/false, /*IsStmt=*/false, + DL->getFilename(), SMLoc()); } void CodeViewDebug::emitCodeViewMagicVersion() { @@ -529,17 +538,6 @@ void CodeViewDebug::emitInlineeLinesSubsection() { endCVSubsection(InlineEnd); } -void CodeViewDebug::collectInlineSiteChildren( - SmallVectorImpl &Children, const FunctionInfo &FI, - const InlineSite &Site) { - for (const DILocation *ChildSiteLoc : Site.ChildSites) { - auto I = FI.InlineSites.find(ChildSiteLoc); - const InlineSite &ChildSite = I->second; - Children.push_back(ChildSite.SiteFuncId); - collectInlineSiteChildren(Children, FI, ChildSite); - } -} - void CodeViewDebug::emitInlinedCallSite(const FunctionInfo &FI, const DILocation *InlinedAt, const InlineSite &Site) { @@ -565,11 +563,9 @@ void CodeViewDebug::emitInlinedCallSite(const FunctionInfo &FI, unsigned FileId = maybeRecordFile(Site.Inlinee->getFile()); unsigned StartLineNum = Site.Inlinee->getLine(); - SmallVector SecondaryFuncIds; - collectInlineSiteChildren(SecondaryFuncIds, FI, Site); OS.EmitCVInlineLinetableDirective(Site.SiteFuncId, FileId, StartLineNum, - FI.Begin, FI.End, SecondaryFuncIds); + FI.Begin, FI.End); OS.EmitLabel(InlineEnd); @@ -877,6 +873,8 @@ void CodeViewDebug::beginFunction(const MachineFunction *MF) { CurFn->FuncId = NextFuncId++; CurFn->Begin = Asm->getFunctionBegin(); + OS.EmitCVFuncIdDirective(CurFn->FuncId); + // Find the end of the function prolog. First known non-DBG_VALUE and // non-frame setup location marks the beginning of the function body. // FIXME: is there a simpler a way to do this? Can we just search diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index 07adfc413d9..7b1c59f1c04 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -222,15 +222,20 @@ public: MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override; bool EmitCVFileDirective(unsigned FileNo, StringRef Filename) override; + bool EmitCVFuncIdDirective(unsigned FuncId) override; + bool EmitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, + unsigned IAFile, unsigned IALine, + unsigned IACol, SMLoc Loc) override; void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName) override; + StringRef FileName, SMLoc Loc) override; void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, const MCSymbol *FnEnd) override; - void EmitCVInlineLinetableDirective( - unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, - const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFunctionIds) override; + void EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym) override; void EmitCVDefRangeDirective( ArrayRef> Ranges, StringRef FixedSizePortion) override; @@ -1114,10 +1119,26 @@ bool MCAsmStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename) { return true; } +bool MCAsmStreamer::EmitCVFuncIdDirective(unsigned FuncId) { + OS << "\t.cv_func_id " << FuncId << '\n'; + return MCStreamer::EmitCVFuncIdDirective(FuncId); +} + +bool MCAsmStreamer::EmitCVInlineSiteIdDirective(unsigned FunctionId, + unsigned IAFunc, + unsigned IAFile, + unsigned IALine, unsigned IACol, + SMLoc Loc) { + OS << "\t.cv_inline_site_id " << FunctionId << " within " << IAFunc + << " inlined_at " << IAFile << ' ' << IALine << ' ' << IACol << '\n'; + return MCStreamer::EmitCVInlineSiteIdDirective(FunctionId, IAFunc, IAFile, + IALine, IACol, Loc); +} + void MCAsmStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName) { + StringRef FileName, SMLoc Loc) { OS << "\t.cv_loc\t" << FunctionId << " " << FileNo << " " << Line << " " << Column; if (PrologueEnd) @@ -1135,12 +1156,12 @@ void MCAsmStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, if (IsVerboseAsm) { OS.PadToColumn(MAI->getCommentColumn()); - OS << MAI->getCommentString() << ' ' << FileName << ':' - << Line << ':' << Column; + OS << MAI->getCommentString() << ' ' << FileName << ':' << Line << ':' + << Column; } EmitEOL(); this->MCStreamer::EmitCVLocDirective(FunctionId, FileNo, Line, Column, - PrologueEnd, IsStmt, FileName); + PrologueEnd, IsStmt, FileName, Loc); } void MCAsmStreamer::EmitCVLinetableDirective(unsigned FunctionId, @@ -1154,24 +1175,19 @@ void MCAsmStreamer::EmitCVLinetableDirective(unsigned FunctionId, this->MCStreamer::EmitCVLinetableDirective(FunctionId, FnStart, FnEnd); } -void MCAsmStreamer::EmitCVInlineLinetableDirective( - unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, - const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFunctionIds) { +void MCAsmStreamer::EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym) { OS << "\t.cv_inline_linetable\t" << PrimaryFunctionId << ' ' << SourceFileId << ' ' << SourceLineNum << ' '; FnStartSym->print(OS, MAI); OS << ' '; FnEndSym->print(OS, MAI); - if (!SecondaryFunctionIds.empty()) { - OS << " contains"; - for (unsigned SecondaryFunctionId : SecondaryFunctionIds) - OS << ' ' << SecondaryFunctionId; - } EmitEOL(); this->MCStreamer::EmitCVInlineLinetableDirective( - PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym, - SecondaryFunctionIds); + PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); } void MCAsmStreamer::EmitCVDefRangeDirective( diff --git a/lib/MC/MCCodeView.cpp b/lib/MC/MCCodeView.cpp index b955511a4a7..acea1ecb2b4 100644 --- a/lib/MC/MCCodeView.cpp +++ b/lib/MC/MCCodeView.cpp @@ -65,6 +65,50 @@ bool CodeViewContext::addFile(unsigned FileNumber, StringRef Filename) { return true; } +bool CodeViewContext::recordFunctionId(unsigned FuncId) { + if (FuncId >= Functions.size()) + Functions.resize(FuncId + 1); + + // Return false if this function info was already allocated. + if (!Functions[FuncId].isUnallocatedFunctionInfo()) + return false; + + // Mark this as an allocated normal function, and leave the rest alone. + Functions[FuncId].ParentFuncIdPlusOne = MCCVFunctionInfo::FunctionSentinel; + return true; +} + +bool CodeViewContext::recordInlinedCallSiteId(unsigned FuncId, unsigned IAFunc, + unsigned IAFile, unsigned IALine, + unsigned IACol) { + if (FuncId >= Functions.size()) + Functions.resize(FuncId + 1); + + // Return false if this function info was already allocated. + if (!Functions[FuncId].isUnallocatedFunctionInfo()) + return false; + + MCCVFunctionInfo::LineInfo InlinedAt; + InlinedAt.File = IAFile; + InlinedAt.Line = IALine; + InlinedAt.Col = IACol; + + // Mark this as an inlined call site and record call site line info. + MCCVFunctionInfo *Info = &Functions[FuncId]; + Info->ParentFuncIdPlusOne = IAFunc + 1; + Info->InlinedAt = InlinedAt; + + // Walk up the call chain adding this function id to the InlinedAtMap of all + // transitive callers until we hit a real function. + while (Info->isInlinedCallSite()) { + InlinedAt = Info->InlinedAt; + Info = getCVFunctionInfo(Info->getParentFuncId()); + Info->InlinedAtMap[FuncId] = InlinedAt; + } + + return true; +} + MCDataFragment *CodeViewContext::getStringTableFragment() { if (!StrTabFragment) { StrTabFragment = new MCDataFragment(); @@ -237,15 +281,17 @@ static uint32_t encodeSignedNumber(uint32_t Data) { return Data << 1; } -void CodeViewContext::emitInlineLineTableForFunction( - MCObjectStreamer &OS, unsigned PrimaryFunctionId, unsigned SourceFileId, - unsigned SourceLineNum, const MCSymbol *FnStartSym, - const MCSymbol *FnEndSym, ArrayRef SecondaryFunctionIds) { +void CodeViewContext::emitInlineLineTableForFunction(MCObjectStreamer &OS, + unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym) { // Create and insert a fragment into the current section that will be encoded // later. - new MCCVInlineLineTableFragment( - PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym, - SecondaryFunctionIds, OS.getCurrentSectionOnly()); + new MCCVInlineLineTableFragment(PrimaryFunctionId, SourceFileId, + SourceLineNum, FnStartSym, FnEndSym, + OS.getCurrentSectionOnly()); } void CodeViewContext::emitDefRange( @@ -280,69 +326,83 @@ void CodeViewContext::encodeInlineLineTable(MCAsmLayout &Layout, size_t LocBegin; size_t LocEnd; std::tie(LocBegin, LocEnd) = getLineExtent(Frag.SiteFuncId); - for (unsigned SecondaryId : Frag.SecondaryFuncs) { - auto Extent = getLineExtent(SecondaryId); + + // Include all child inline call sites in our .cv_loc extent. + MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(Frag.SiteFuncId); + for (auto &KV : SiteInfo->InlinedAtMap) { + unsigned ChildId = KV.first; + auto Extent = getLineExtent(ChildId); LocBegin = std::min(LocBegin, Extent.first); LocEnd = std::max(LocEnd, Extent.second); } + if (LocBegin >= LocEnd) return; ArrayRef Locs = getLinesForExtent(LocBegin, LocEnd); if (Locs.empty()) return; - SmallSet InlinedFuncIds; - InlinedFuncIds.insert(Frag.SiteFuncId); - InlinedFuncIds.insert(Frag.SecondaryFuncs.begin(), Frag.SecondaryFuncs.end()); - // Make an artificial start location using the function start and the inlinee // lines start location information. All deltas start relative to this // location. MCCVLineEntry StartLoc(Frag.getFnStartSym(), MCCVLoc(Locs.front())); StartLoc.setFileNum(Frag.StartFileId); StartLoc.setLine(Frag.StartLineNum); - const MCCVLineEntry *LastLoc = &StartLoc; bool HaveOpenRange = false; + const MCSymbol *LastLabel = Frag.getFnStartSym(); + MCCVFunctionInfo::LineInfo LastSourceLoc, CurSourceLoc; + LastSourceLoc.File = Frag.StartFileId; + LastSourceLoc.Line = Frag.StartLineNum; + SmallVectorImpl &Buffer = Frag.getContents(); Buffer.clear(); // Clear old contents if we went through relaxation. for (const MCCVLineEntry &Loc : Locs) { - if (!InlinedFuncIds.count(Loc.getFunctionId())) { - // We've hit a cv_loc not attributed to this inline call site. Use this - // label to end the PC range. - if (HaveOpenRange) { - unsigned Length = - computeLabelDiff(Layout, LastLoc->getLabel(), Loc.getLabel()); - compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer); - compressAnnotation(Length, Buffer); + if (Loc.getFunctionId() == Frag.SiteFuncId) { + CurSourceLoc.File = Loc.getFileNum(); + CurSourceLoc.Line = Loc.getLine(); + } else { + auto I = SiteInfo->InlinedAtMap.find(Loc.getFunctionId()); + if (I != SiteInfo->InlinedAtMap.end()) { + // This .cv_loc is from a child inline call site. Use the source + // location of the inlined call site instead of the .cv_loc directive + // source location. + CurSourceLoc = I->second; + } else { + // We've hit a cv_loc not attributed to this inline call site. Use this + // label to end the PC range. + if (HaveOpenRange) { + unsigned Length = computeLabelDiff(Layout, LastLabel, Loc.getLabel()); + compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer); + compressAnnotation(Length, Buffer); + LastLabel = Loc.getLabel(); + } + HaveOpenRange = false; + continue; } - HaveOpenRange = false; - continue; } - // If we've already opened the function and we're at an indirectly inlined - // location, continue until the next directly inlined location. - bool DirectlyInlined = Loc.getFunctionId() == Frag.SiteFuncId; - if (!DirectlyInlined && HaveOpenRange) + // Skip this .cv_loc if we have an open range and this isn't a meaningful + // source location update. The current table format does not support column + // info, so we can skip updates for those. + if (HaveOpenRange && CurSourceLoc.File == LastSourceLoc.File && + CurSourceLoc.Line == LastSourceLoc.Line) continue; + HaveOpenRange = true; - if (Loc.getFileNum() != LastLoc->getFileNum()) { + if (CurSourceLoc.File != LastSourceLoc.File) { // File ids are 1 based, and each file checksum table entry is 8 bytes // long. See emitFileChecksums above. - unsigned FileOffset = 8 * (Loc.getFileNum() - 1); + unsigned FileOffset = 8 * (CurSourceLoc.File - 1); compressAnnotation(BinaryAnnotationsOpCode::ChangeFile, Buffer); compressAnnotation(FileOffset, Buffer); } - int LineDelta = Loc.getLine() - LastLoc->getLine(); - if (LineDelta == 0) - continue; - + int LineDelta = CurSourceLoc.Line - LastSourceLoc.Line; unsigned EncodedLineDelta = encodeSignedNumber(LineDelta); - unsigned CodeDelta = - computeLabelDiff(Layout, LastLoc->getLabel(), Loc.getLabel()); - if (CodeDelta == 0) { + unsigned CodeDelta = computeLabelDiff(Layout, LastLabel, Loc.getLabel()); + if (CodeDelta == 0 && LineDelta != 0) { compressAnnotation(BinaryAnnotationsOpCode::ChangeLineOffset, Buffer); compressAnnotation(EncodedLineDelta, Buffer); } else if (EncodedLineDelta < 0x8 && CodeDelta <= 0xf) { @@ -355,29 +415,29 @@ void CodeViewContext::encodeInlineLineTable(MCAsmLayout &Layout, compressAnnotation(Operand, Buffer); } else { // Otherwise use the separate line and code deltas. - compressAnnotation(BinaryAnnotationsOpCode::ChangeLineOffset, Buffer); - compressAnnotation(EncodedLineDelta, Buffer); + if (LineDelta != 0) { + compressAnnotation(BinaryAnnotationsOpCode::ChangeLineOffset, Buffer); + compressAnnotation(EncodedLineDelta, Buffer); + } compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeOffset, Buffer); compressAnnotation(CodeDelta, Buffer); } - LastLoc = &Loc; + LastLabel = Loc.getLabel(); + LastSourceLoc = CurSourceLoc; } assert(HaveOpenRange); unsigned EndSymLength = - computeLabelDiff(Layout, LastLoc->getLabel(), Frag.getFnEndSym()); + computeLabelDiff(Layout, LastLabel, Frag.getFnEndSym()); unsigned LocAfterLength = ~0U; ArrayRef LocAfter = getLinesForExtent(LocEnd, LocEnd + 1); if (!LocAfter.empty()) { // Only try to compute this difference if we're in the same section. const MCCVLineEntry &Loc = LocAfter[0]; - if (&Loc.getLabel()->getSection(false) == - &LastLoc->getLabel()->getSection(false)) { - LocAfterLength = - computeLabelDiff(Layout, LastLoc->getLabel(), Loc.getLabel()); - } + if (&Loc.getLabel()->getSection(false) == &LastLabel->getSection(false)) + LocAfterLength = computeLabelDiff(Layout, LastLabel, Loc.getLabel()); } compressAnnotation(BinaryAnnotationsOpCode::ChangeCodeLength, Buffer); diff --git a/lib/MC/MCObjectStreamer.cpp b/lib/MC/MCObjectStreamer.cpp index e86a0e1dcf9..f4cb09494d0 100644 --- a/lib/MC/MCObjectStreamer.cpp +++ b/lib/MC/MCObjectStreamer.cpp @@ -369,13 +369,13 @@ void MCObjectStreamer::EmitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, void MCObjectStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName) { + StringRef FileName, SMLoc Loc) { // 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); + PrologueEnd, IsStmt, FileName, Loc); } void MCObjectStreamer::EmitCVLinetableDirective(unsigned FunctionId, @@ -388,14 +388,12 @@ void MCObjectStreamer::EmitCVLinetableDirective(unsigned FunctionId, void MCObjectStreamer::EmitCVInlineLinetableDirective( unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, - const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFunctionIds) { + const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) { getContext().getCVContext().emitInlineLineTableForFunction( *this, PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, - FnEndSym, SecondaryFunctionIds); + FnEndSym); this->MCStreamer::EmitCVInlineLinetableDirective( - PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym, - SecondaryFunctionIds); + PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); } void MCObjectStreamer::EmitCVDefRangeDirective( diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index 43c88c96860..a75818e99cd 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -378,6 +378,9 @@ private: bool parseRegisterOrRegisterNumber(int64_t &Register, SMLoc DirectiveLoc); + bool parseCVFunctionId(int64_t &FunctionId, StringRef DirectiveName); + bool parseCVFileId(int64_t &FileId, StringRef DirectiveName); + // Generic (target and platform independent) directive parsing. enum DirectiveKind { DK_NO_DIRECTIVE, // Placeholder @@ -397,8 +400,9 @@ 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_INLINE_LINETABLE, - DK_CV_DEF_RANGE, DK_CV_STRINGTABLE, DK_CV_FILECHECKSUMS, + DK_CV_FILE, DK_CV_FUNC_ID, DK_CV_INLINE_SITE_ID, DK_CV_LOC, DK_CV_LINETABLE, + DK_CV_INLINE_LINETABLE, DK_CV_DEF_RANGE, 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, @@ -436,9 +440,11 @@ private: bool parseDirectiveLoc(); bool parseDirectiveStabs(); - // ".cv_file", ".cv_loc", ".cv_linetable", "cv_inline_linetable", - // ".cv_def_range" + // ".cv_file", ".cv_func_id", ".cv_inline_site_id", ".cv_loc", ".cv_linetable", + // ".cv_inline_linetable", ".cv_def_range" bool parseDirectiveCVFile(); + bool parseDirectiveCVFuncId(); + bool parseDirectiveCVInlineSiteId(); bool parseDirectiveCVLoc(); bool parseDirectiveCVLinetable(); bool parseDirectiveCVInlineLinetable(); @@ -1790,6 +1796,10 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info, return parseDirectiveStabs(); case DK_CV_FILE: return parseDirectiveCVFile(); + case DK_CV_FUNC_ID: + return parseDirectiveCVFuncId(); + case DK_CV_INLINE_SITE_ID: + return parseDirectiveCVInlineSiteId(); case DK_CV_LOC: return parseDirectiveCVLoc(); case DK_CV_LINETABLE: @@ -3240,6 +3250,107 @@ bool AsmParser::parseDirectiveCVFile() { return false; } +bool AsmParser::parseCVFunctionId(int64_t &FunctionId, + StringRef DirectiveName) { + SMLoc Loc; + return parseTokenLoc(Loc) || + parseIntToken(FunctionId, "expected function id in '" + DirectiveName + + "' directive") || + check(FunctionId < 0 || FunctionId >= UINT_MAX, Loc, + "expected function id within range [0, UINT_MAX)"); +} + +bool AsmParser::parseCVFileId(int64_t &FileNumber, StringRef DirectiveName) { + SMLoc Loc; + return parseTokenLoc(Loc) || + parseIntToken(FileNumber, "expected integer in '" + DirectiveName + + "' directive") || + check(FileNumber < 1, Loc, "file number less than one in '" + + DirectiveName + "' directive") || + check(!getCVContext().isValidFileNumber(FileNumber), Loc, + "unassigned file number in '" + DirectiveName + "' directive"); +} + +/// parseDirectiveCVFuncId +/// ::= .cv_func_id FunctionId +/// +/// Introduces a function ID that can be used with .cv_loc. +bool AsmParser::parseDirectiveCVFuncId() { + SMLoc FunctionIdLoc = getTok().getLoc(); + int64_t FunctionId; + + if (parseCVFunctionId(FunctionId, ".cv_func_id") || + parseToken(AsmToken::EndOfStatement, + "unexpected token in '.cv_func_id' directive")) + return true; + + if (!getStreamer().EmitCVFuncIdDirective(FunctionId)) + Error(FunctionIdLoc, "function id already allocated"); + + return false; +} + +/// parseDirectiveCVInlineSiteId +/// ::= .cv_inline_site_id FunctionId +/// "within" IAFunc +/// "inlined_at" IAFile IALine [IACol] +/// +/// Introduces a function ID that can be used with .cv_loc. Includes "inlined +/// at" source location information for use in the line table of the caller, +/// whether the caller is a real function or another inlined call site. +bool AsmParser::parseDirectiveCVInlineSiteId() { + SMLoc FunctionIdLoc = getTok().getLoc(); + int64_t FunctionId; + int64_t IAFunc; + int64_t IAFile; + int64_t IALine; + int64_t IACol = 0; + + // FunctionId + if (parseCVFunctionId(FunctionId, ".cv_inline_site_id")) + return true; + + // "within" + if (check((getLexer().isNot(AsmToken::Identifier) || + getTok().getIdentifier() != "within"), + "expected 'within' identifier in '.cv_inline_site_id' directive")) + return true; + Lex(); + + // IAFunc + if (parseCVFunctionId(IAFunc, ".cv_inline_site_id")) + return true; + + // "inlined_at" + if (check((getLexer().isNot(AsmToken::Identifier) || + getTok().getIdentifier() != "inlined_at"), + "expected 'inlined_at' identifier in '.cv_inline_site_id' " + "directive") ) + return true; + Lex(); + + // IAFile IALine + if (parseCVFileId(IAFile, ".cv_inline_site_id") || + parseIntToken(IALine, "expected line number after 'inlined_at'")) + return true; + + // [IACol] + if (getLexer().is(AsmToken::Integer)) { + IACol = getTok().getIntVal(); + Lex(); + } + + if (parseToken(AsmToken::EndOfStatement, + "unexpected token in '.cv_inline_site_id' directive")) + return true; + + if (!getStreamer().EmitCVInlineSiteIdDirective(FunctionId, IAFunc, IAFile, + IALine, IACol, FunctionIdLoc)) + Error(FunctionIdLoc, "function id already allocated"); + + return false; +} + /// parseDirectiveCVLoc /// ::= .cv_loc FunctionId FileNumber [LineNumber] [ColumnPos] [prologue_end] /// [is_stmt VALUE] @@ -3248,18 +3359,11 @@ bool AsmParser::parseDirectiveCVFile() { /// third number is a column position (zero if not specified). The remaining /// optional items are .loc sub-directives. bool AsmParser::parseDirectiveCVLoc() { + SMLoc DirectiveLoc = getTok().getLoc(); SMLoc Loc; int64_t FunctionId, FileNumber; - if (parseTokenLoc(Loc) || - parseIntToken(FunctionId, "unexpected token in '.cv_loc' directive") || - check(FunctionId < 0, Loc, - "function id less than zero in '.cv_loc' directive") || - parseTokenLoc(Loc) || - parseIntToken(FileNumber, "expected integer in '.cv_loc' directive") || - check(FileNumber < 1, Loc, - "file number less than one in '.cv_loc' directive") || - check(!getCVContext().isValidFileNumber(FileNumber), Loc, - "unassigned file number in '.cv_loc' directive")) + if (parseCVFunctionId(FunctionId, ".cv_loc") || + parseCVFileId(FileNumber, ".cv_loc")) return true; int64_t LineNumber = 0; @@ -3307,7 +3411,8 @@ bool AsmParser::parseDirectiveCVLoc() { Lex(); getStreamer().EmitCVLocDirective(FunctionId, FileNumber, LineNumber, - ColumnPos, PrologueEnd, IsStmt, StringRef()); + ColumnPos, PrologueEnd, IsStmt, StringRef(), + DirectiveLoc); return false; } @@ -3317,10 +3422,7 @@ bool AsmParser::parseDirectiveCVLinetable() { int64_t FunctionId; StringRef FnStartName, FnEndName; SMLoc Loc = getTok().getLoc(); - if (parseIntToken(FunctionId, - "expected Integer in '.cv_linetable' directive") || - check(FunctionId < 0, Loc, - "function id less than zero in '.cv_linetable' directive") || + if (parseCVFunctionId(FunctionId, ".cv_linetable") || parseToken(AsmToken::Comma, "unexpected token in '.cv_linetable' directive") || parseTokenLoc(Loc) || check(parseIdentifier(FnStartName), Loc, @@ -3340,16 +3442,11 @@ bool AsmParser::parseDirectiveCVLinetable() { /// parseDirectiveCVInlineLinetable /// ::= .cv_inline_linetable PrimaryFunctionId FileId LineNum FnStart FnEnd -/// ("contains" SecondaryFunctionId+)? bool AsmParser::parseDirectiveCVInlineLinetable() { int64_t PrimaryFunctionId, SourceFileId, SourceLineNum; StringRef FnStartName, FnEndName; SMLoc Loc = getTok().getLoc(); - if (parseIntToken( - PrimaryFunctionId, - "expected PrimaryFunctionId in '.cv_inline_linetable' directive") || - check(PrimaryFunctionId < 0, Loc, - "function id less than zero in '.cv_inline_linetable' directive") || + if (parseCVFunctionId(PrimaryFunctionId, ".cv_inline_linetable") || parseTokenLoc(Loc) || parseIntToken( SourceFileId, @@ -3368,24 +3465,6 @@ bool AsmParser::parseDirectiveCVInlineLinetable() { "expected identifier in directive")) return true; - 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); - } - } - if (parseToken(AsmToken::EndOfStatement, "Expected End of Statement")) return true; @@ -3393,7 +3472,7 @@ bool AsmParser::parseDirectiveCVInlineLinetable() { MCSymbol *FnEndSym = getContext().getOrCreateSymbol(FnEndName); getStreamer().EmitCVInlineLinetableDirective(PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, - FnEndSym, SecondaryFunctionIds); + FnEndSym); return false; } @@ -4701,9 +4780,11 @@ void AsmParser::initializeDirectiveKindMap() { DirectiveKindMap[".loc"] = DK_LOC; DirectiveKindMap[".stabs"] = DK_STABS; DirectiveKindMap[".cv_file"] = DK_CV_FILE; + DirectiveKindMap[".cv_func_id"] = DK_CV_FUNC_ID; DirectiveKindMap[".cv_loc"] = DK_CV_LOC; DirectiveKindMap[".cv_linetable"] = DK_CV_LINETABLE; DirectiveKindMap[".cv_inline_linetable"] = DK_CV_INLINE_LINETABLE; + DirectiveKindMap[".cv_inline_site_id"] = DK_CV_INLINE_SITE_ID; DirectiveKindMap[".cv_def_range"] = DK_CV_DEF_RANGE; DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE; DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS; diff --git a/lib/MC/MCStreamer.cpp b/lib/MC/MCStreamer.cpp index ca244126dcd..124a1911a9a 100644 --- a/lib/MC/MCStreamer.cpp +++ b/lib/MC/MCStreamer.cpp @@ -220,22 +220,54 @@ bool MCStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename) { return getContext().getCVContext().addFile(FileNo, Filename); } +bool MCStreamer::EmitCVFuncIdDirective(unsigned FunctionId) { + return getContext().getCVContext().recordFunctionId(FunctionId); +} + +bool MCStreamer::EmitCVInlineSiteIdDirective(unsigned FunctionId, + unsigned IAFunc, unsigned IAFile, + unsigned IALine, unsigned IACol, + SMLoc Loc) { + if (getContext().getCVContext().getCVFunctionInfo(IAFunc) == nullptr) { + getContext().reportError(Loc, "parent function id not introduced by " + ".cv_func_id or .cv_inline_site_id"); + return true; + } + + return getContext().getCVContext().recordInlinedCallSiteId( + FunctionId, IAFunc, IAFile, IALine, IACol); +} + void MCStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName) { - getContext().getCVContext().setCurrentCVLoc(FunctionId, FileNo, Line, Column, - PrologueEnd, IsStmt); + StringRef FileName, SMLoc Loc) { + CodeViewContext &CVC = getContext().getCVContext(); + MCCVFunctionInfo *FI = CVC.getCVFunctionInfo(FunctionId); + if (!FI) + return getContext().reportError( + Loc, "function id not introduced by .cv_func_id or .cv_inline_site_id"); + + // Track the section + if (FI->Section == nullptr) + FI->Section = getCurrentSectionOnly(); + else if (FI->Section != getCurrentSectionOnly()) + return getContext().reportError( + Loc, + "all .cv_loc directives for a function must be in the same section"); + + CVC.setCurrentCVLoc(FunctionId, FileNo, Line, Column, PrologueEnd, IsStmt); } void MCStreamer::EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, const MCSymbol *End) {} -void MCStreamer::EmitCVInlineLinetableDirective( - unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, - const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, - ArrayRef SecondaryFunctionIds) {} +void MCStreamer::EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym) {} void MCStreamer::EmitCVDefRangeDirective( ArrayRef> Ranges, diff --git a/test/DebugInfo/COFF/inlining-header.ll b/test/DebugInfo/COFF/inlining-header.ll index 6a57e182a32..143bd48e4be 100644 --- a/test/DebugInfo/COFF/inlining-header.ll +++ b/test/DebugInfo/COFF/inlining-header.ll @@ -25,13 +25,16 @@ ; ASM: _main: # @main ; ASM: Lfunc_begin0: +; ASM: .cv_func_id 0 ; ASM: # BB#0: # %entry ; ASM: .cv_file 1 "D:\\src\\llvm\\build\\t.cpp" ; ASM: .cv_loc 0 1 9 5 is_stmt 0 # t.cpp:9:5 ; ASM: incl "?x@@3HC" +; ASM: .cv_inline_site_id 1 within 0 inlined_at 1 10 3 ; ASM: .cv_loc 1 1 4 5 # t.cpp:4:5 ; ASM: addl $2, "?x@@3HC" ; ASM: .cv_file 2 "D:\\src\\llvm\\build\\t.h" +; ASM: .cv_inline_site_id 2 within 1 inlined_at 1 5 3 ; ASM: .cv_loc 2 2 2 5 # ./t.h:2:5 ; ASM: addl $3, "?x@@3HC" ; ASM: .cv_loc 1 1 6 5 # t.cpp:6:5 @@ -61,7 +64,6 @@ ; OBJ: Subsection [ ; OBJ: SubSectionType: Symbols (0xF1) -; OBJ: SubSectionSize: 0x62 ; OBJ: ProcStart { ; OBJ: Kind: S_GPROC32_ID (0x1147) ; OBJ: FunctionType: main (0x1005) @@ -78,7 +80,8 @@ ; OBJ: Inlinee: g (0x1002) ; OBJ: BinaryAnnotations [ ; OBJ-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x6, LineOffset: 1} -; OBJ-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0xE, LineOffset: 2} +; OBJ-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x7, LineOffset: 1} +; OBJ-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x7, LineOffset: 1} ; OBJ-NEXT: ChangeCodeLength: 0x7 ; OBJ-NEXT: ] ; OBJ: } diff --git a/test/DebugInfo/COFF/inlining-levels.ll b/test/DebugInfo/COFF/inlining-levels.ll index 2c25670dee5..55ce3de8479 100644 --- a/test/DebugInfo/COFF/inlining-levels.ll +++ b/test/DebugInfo/COFF/inlining-levels.ll @@ -20,13 +20,13 @@ ; OBJ: SubSectionType: Symbols (0xF1) ; OBJ: ProcStart { ; OBJ: InlineSite { -; OBJ: Inlinee: h (0x1004) +; OBJ: Inlinee: h (0x1002) ; OBJ: } ; OBJ: InlineSite { ; OBJ: Inlinee: g (0x1003) ; OBJ: } ; OBJ: InlineSite { -; OBJ: Inlinee: f (0x1002) +; OBJ: Inlinee: f (0x1004) ; OBJ: } ; OBJ: InlineSiteEnd { ; OBJ: } diff --git a/test/DebugInfo/COFF/inlining.ll b/test/DebugInfo/COFF/inlining.ll index 815111f9cbf..9ca4c4120a0 100644 --- a/test/DebugInfo/COFF/inlining.ll +++ b/test/DebugInfo/COFF/inlining.ll @@ -21,11 +21,14 @@ ; 16: x += 7; ; 17: } +; ASM: .cv_func_id 0 ; ASM: .cv_loc 0 1 13 0 is_stmt 0 # t.cpp:13:0 ; ASM: .cv_loc 0 1 14 5 # t.cpp:14:5 ; ASM: addl $6, "?x@@3HC" +; ASM: .cv_inline_site_id 1 within 0 inlined_at 1 15 3 ; ASM: .cv_loc 1 1 9 5 # t.cpp:9:5 ; ASM: addl $4, "?x@@3HC" +; ASM: .cv_inline_site_id 2 within 1 inlined_at 1 10 3 ; ASM: .cv_loc 2 1 3 7 # t.cpp:3:7 ; ASM: .cv_loc 2 1 4 5 # t.cpp:4:5 ; ASM: addl {{.*}}, "?x@@3HC" @@ -60,7 +63,7 @@ ; ASM: .long ; ASM: .long ; ASM: .long -; ASM: .cv_inline_linetable 1 1 8 Lfunc_begin0 Lfunc_end0 contains 2 +; ASM: .cv_inline_linetable 1 1 8 Lfunc_begin0 Lfunc_end0 ; ASM: .short 4429 ; ASM: .long ; ASM: .long @@ -184,8 +187,9 @@ ; OBJ: Inlinee: bar (0x1002) ; OBJ: BinaryAnnotations [ ; OBJ-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x8, LineOffset: 1} -; OBJ-NEXT: ChangeLineOffset: 2 -; OBJ-NEXT: ChangeCodeOffset: 0x25 +; OBJ-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x7, LineOffset: 1} +; OBJ-NEXT: ChangeLineOffset: 1 +; OBJ-NEXT: ChangeCodeOffset: 0x1E ; OBJ-NEXT: ChangeCodeLength: 0x7 ; OBJ: ] ; OBJ: } diff --git a/test/DebugInfo/COFF/local-variables.ll b/test/DebugInfo/COFF/local-variables.ll index 86356fdde45..2c3fd89a9d4 100644 --- a/test/DebugInfo/COFF/local-variables.ll +++ b/test/DebugInfo/COFF/local-variables.ll @@ -22,6 +22,7 @@ ; 17: } ; ASM: f: # @f +; ASM: .cv_func_id 0 ; ASM: .cv_file 1 "D:\\src\\llvm\\build\\t.cpp" ; ASM: .cv_loc 0 1 7 0 is_stmt 0 # t.cpp:7:0 ; ASM: .seh_proc f @@ -40,6 +41,7 @@ ; ASM: .cv_loc 0 1 9 9 # t.cpp:9:9 ; ASM: movl $42, 40(%rsp) ; ASM: [[inline_site1:\.Ltmp.*]]: +; ASM: .cv_inline_site_id 1 within 0 inlined_at 1 10 5 ; ASM: .cv_loc 1 1 4 7 # t.cpp:4:7 ; ASM: movl $3, 44(%rsp) ; ASM: leaq 44(%rsp), %rcx @@ -54,6 +56,7 @@ ; ASM: .cv_loc 0 1 13 9 # t.cpp:13:9 ; ASM: movl $42, 36(%rsp) ; ASM: [[inline_site2:\.Ltmp.*]]: +; ASM: .cv_inline_site_id 2 within 0 inlined_at 1 14 5 ; ASM: .cv_loc 2 1 4 7 # t.cpp:4:7 ; ASM: movl $3, 48(%rsp) ; ASM: leaq 48(%rsp), %rcx diff --git a/test/MC/COFF/cv-def-range.s b/test/MC/COFF/cv-def-range.s index a1ae1404e92..5ac0df7f7d9 100644 --- a/test/MC/COFF/cv-def-range.s +++ b/test/MC/COFF/cv-def-range.s @@ -15,6 +15,7 @@ _g: # @g Lfunc_begin0: .cv_file 1 "\\usr\\local\\google\\home\\majnemer\\llvm\\src\\" + .cv_func_id 0 .cv_loc 0 1 3 0 is_stmt 0 # :3:0 # BB#0: # %entry pushl %ebp diff --git a/test/MC/COFF/cv-empty-linetable.s b/test/MC/COFF/cv-empty-linetable.s index 4fcafd2d268..568d55a3657 100644 --- a/test/MC/COFF/cv-empty-linetable.s +++ b/test/MC/COFF/cv-empty-linetable.s @@ -16,6 +16,7 @@ _f: # @f Lfunc_begin0: # BB#0: # %entry .cv_file 1 "cv-empty-linetable.s" + .cv_func_id 1 .cv_loc 1 1 3 15 is_stmt 0 jmp _g # TAILCALL Lfunc_end0: diff --git a/test/MC/COFF/cv-errors.s b/test/MC/COFF/cv-errors.s new file mode 100644 index 00000000000..ca2f77549c5 --- /dev/null +++ b/test/MC/COFF/cv-errors.s @@ -0,0 +1,56 @@ +# RUN: not llvm-mc %s -o /dev/null 2>&1 | FileCheck %s + +.text +foo: +.cv_file a +# CHECK: error: expected file number in '.cv_file' directive +# CHECK-NOT: error: +.cv_file 0 "t.cpp" +# CHECK: error: file number less than one +# CHECK-NOT: error: +.cv_func_id x +# CHECK: error: expected function id in '.cv_func_id' directive +# CHECK-NOT: error: +.cv_func_id -1 +# CHECK: error: expected function id in '.cv_func_id' directive +# CHECK-NOT: error: +.cv_func_id 0xFFFFFFFFFFFFFFFF +# CHECK: error: expected function id within range [0, UINT_MAX) +# CHECK-NOT: error: +.cv_inline_site_id x +# CHECK: error: expected function id in '.cv_inline_site_id' directive +# CHECK-NOT: error: + +.cv_file 1 "t.cpp" +.cv_func_id 0 + +.cv_inline_site_id 0 0 0 0 0 0 +# CHECK: error: expected 'within' identifier in '.cv_inline_site_id' directive +# CHECK-NOT: error: + +.cv_inline_site_id 0 within a +# CHECK: error: expected function id in '.cv_inline_site_id' directive +# CHECK-NOT: error: + +.cv_inline_site_id 0 within 0 x +# CHECK: error: expected 'inlined_at' identifier in '.cv_inline_site_id' directive +# CHECK-NOT: error: + +.cv_inline_site_id 0 within 0 inlined_at 0 0 0 +# CHECK: error: file number less than one in '.cv_inline_site_id' directive +# CHECK-NOT: error: + +.cv_inline_site_id 0 within 0 inlined_at 10 0 0 +# CHECK: error: unassigned file number in '.cv_inline_site_id' directive +# CHECK-NOT: error: + +.cv_inline_site_id 0 within 0 inlined_at 1 1 1 +# CHECK: error: function id already allocated +# CHECK-NOT: error: + +.cv_inline_site_id 1 within 0 inlined_at 1 1 1 + +.cv_loc 0 1 1 1 # t.cpp:1:1 +nop +.cv_loc 1 1 1 1 # t.cpp:1:1 +nop diff --git a/test/MC/COFF/cv-inline-linetable-infloop.s b/test/MC/COFF/cv-inline-linetable-infloop.s index cd0a073be2a..804ed6f404d 100644 --- a/test/MC/COFF/cv-inline-linetable-infloop.s +++ b/test/MC/COFF/cv-inline-linetable-infloop.s @@ -19,6 +19,8 @@ .p2align 4, 0x90 infloop: # @infloop .Lfunc_begin1: + .cv_func_id 0 + .cv_inline_site_id 2 within 0 inlined_at 1 1 1 .cv_loc 2 1 3 7 # t.c:3:7 jmp .Lfunc_begin1 .Lfunc_end1: @@ -31,6 +33,7 @@ infloop: # @infloop .globl afterinfloop .p2align 4, 0x90 afterinfloop: # @afterinfloop + .cv_func_id 3 .cv_loc 3 1 13 0 # t.c:13:0 retq diff --git a/test/MC/COFF/cv-inline-linetable-unlikely.s b/test/MC/COFF/cv-inline-linetable-unlikely.s new file mode 100644 index 00000000000..dd3a66f419c --- /dev/null +++ b/test/MC/COFF/cv-inline-linetable-unlikely.s @@ -0,0 +1,191 @@ +# RUN: llvm-mc -triple=x86_64-windows -filetype=obj < %s | llvm-readobj -codeview | FileCheck %s + +# C source to generate the assembly: +# volatile int unlikely_cond = 0; +# extern void __declspec(noreturn) abort(); +# __forceinline void f() { +# if (unlikely_cond) +# abort(); +# } +# void g() { +# unlikely_cond = 0; +# f(); +# unlikely_cond = 0; +# } + +# This test is interesting because the inlined instructions are discontiguous. +# LLVM's block layout algorithms will put the 'abort' call last, as it is +# considered highly unlikely to execute. This is similar to what it does for +# calls to __asan_report*, for which it is very important to have an accurate +# stack trace. + +# CHECK: ProcStart { +# CHECK: FunctionType: g (0x1003) +# CHECK: CodeOffset: g+0x0 +# CHECK: DisplayName: g +# CHECK: LinkageName: g +# CHECK: } +# CHECK: InlineSite { +# CHECK: Inlinee: f (0x1002) +# CHECK: BinaryAnnotations [ +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0xE, LineOffset: 1} +# CHECK-NEXT: ChangeCodeLength: 0x9 +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0xF, LineOffset: 1} +# CHECK-NEXT: ChangeCodeLength: 0x7 +# CHECK-NEXT: ] + + .text + .globl g +g: # @g +.Lfunc_begin0: + .cv_func_id 0 + .cv_file 1 "C:\\src\\llvm\\build\\t.cpp" + .cv_loc 0 1 7 0 is_stmt 0 # t.cpp:7:0 +.seh_proc g + subq $40, %rsp + .seh_stackalloc 40 + .seh_endprologue + .cv_loc 0 1 8 17 # t.cpp:8:17 + movl $0, unlikely_cond(%rip) + .cv_inline_site_id 1 within 0 inlined_at 1 9 3 + .cv_loc 1 1 4 7 # t.cpp:4:7 + cmpl $0, unlikely_cond(%rip) + jne .LBB0_1 + .cv_loc 0 1 10 17 # t.cpp:10:17 + movl $0, unlikely_cond(%rip) + .cv_loc 0 1 11 1 # t.cpp:11:1 + addq $40, %rsp + retq + +.LBB0_1: # %if.then.i + .cv_loc 1 1 5 5 # t.cpp:5:5 + callq abort + ud2 +.Lfunc_end0: + .seh_handlerdata + .text + .seh_endproc + + .bss + .globl unlikely_cond # @unlikely_cond + .p2align 2 +unlikely_cond: + .long 0 # 0x0 + + .section .debug$S,"dr" + .p2align 2 + .long 4 # Debug section magic + .long 246 # Inlinee lines subsection + .long .Ltmp9-.Ltmp8 # Subsection size +.Ltmp8: + .long 0 # Inlinee lines signature + + # Inlined function f starts at t.cpp:3 + .long 4098 # Type index of inlined function + .long 0 # Offset into filechecksum table + .long 3 # Starting line number +.Ltmp9: + .p2align 2 + .long 241 # Symbol subsection for g + .long .Ltmp11-.Ltmp10 # Subsection size +.Ltmp10: + .short .Ltmp13-.Ltmp12 # Record length +.Ltmp12: + .short 4423 # Record kind: S_GPROC32_ID + .long 0 # PtrParent + .long 0 # PtrEnd + .long 0 # PtrNext + .long .Lfunc_end0-g # Code size + .long 0 # Offset after prologue + .long 0 # Offset before epilogue + .long 4099 # Function type index + .secrel32 g # Function section relative address + .secidx g # Function section index + .byte 0 # Flags + .asciz "g" # Function name +.Ltmp13: + .short .Ltmp15-.Ltmp14 # Record length +.Ltmp14: + .short 4429 # Record kind: S_INLINESITE + .long 0 # PtrParent + .long 0 # PtrEnd + .long 4098 # Inlinee type index + .cv_inline_linetable 1 1 3 .Lfunc_begin0 .Lfunc_end0 +.Ltmp15: + .short 2 # Record length + .short 4430 # Record kind: S_INLINESITE_END + .short 2 # Record length + .short 4431 # Record kind: S_PROC_ID_END +.Ltmp11: + .p2align 2 + .cv_linetable 0, g, .Lfunc_end0 + .long 241 # Symbol subsection for globals + .long .Ltmp17-.Ltmp16 # Subsection size +.Ltmp16: + .short .Ltmp19-.Ltmp18 # Record length +.Ltmp18: + .short 4365 # Record kind: S_GDATA32 + .long 4100 # Type + .secrel32 unlikely_cond # DataOffset + .secidx unlikely_cond # Segment + .asciz "unlikely_cond" # Name +.Ltmp19: +.Ltmp17: + .p2align 2 + .cv_filechecksums # File index to string table offset subsection + .cv_stringtable # String table + .section .debug$T,"dr" + .p2align 2 + .long 4 # Debug section magic + # ArgList (0x1000) { + # TypeLeafKind: LF_ARGLIST (0x1201) + # NumArgs: 0 + # Arguments [ + # ] + # } + .byte 0x06, 0x00, 0x01, 0x12 + .byte 0x00, 0x00, 0x00, 0x00 + # Procedure (0x1001) { + # TypeLeafKind: LF_PROCEDURE (0x1008) + # ReturnType: void (0x3) + # CallingConvention: NearC (0x0) + # FunctionOptions [ (0x0) + # ] + # NumParameters: 0 + # ArgListType: () (0x1000) + # } + .byte 0x0e, 0x00, 0x08, 0x10 + .byte 0x03, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x10, 0x00, 0x00 + # FuncId (0x1002) { + # TypeLeafKind: LF_FUNC_ID (0x1601) + # ParentScope: 0x0 + # FunctionType: void () (0x1001) + # Name: f + # } + .byte 0x0e, 0x00, 0x01, 0x16 + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x01, 0x10, 0x00, 0x00 + .byte 0x66, 0x00, 0xf2, 0xf1 + # FuncId (0x1003) { + # TypeLeafKind: LF_FUNC_ID (0x1601) + # ParentScope: 0x0 + # FunctionType: void () (0x1001) + # Name: g + # } + .byte 0x0e, 0x00, 0x01, 0x16 + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x01, 0x10, 0x00, 0x00 + .byte 0x67, 0x00, 0xf2, 0xf1 + # Modifier (0x1004) { + # TypeLeafKind: LF_MODIFIER (0x1001) + # ModifiedType: int (0x74) + # Modifiers [ (0x2) + # Volatile (0x2) + # ] + # } + .byte 0x0a, 0x00, 0x01, 0x10 + .byte 0x74, 0x00, 0x00, 0x00 + .byte 0x02, 0x00, 0xf2, 0xf1 + diff --git a/test/MC/COFF/cv-inline-linetable-unreachable.s b/test/MC/COFF/cv-inline-linetable-unreachable.s index eb89dd51927..0f29d1667c3 100644 --- a/test/MC/COFF/cv-inline-linetable-unreachable.s +++ b/test/MC/COFF/cv-inline-linetable-unreachable.s @@ -15,6 +15,8 @@ _g: # @g Lfunc_begin0: .cv_file 1 "\\usr\\local\\google\\home\\majnemer\\llvm\\src\\" + .cv_func_id 0 + .cv_inline_site_id 1 within 0 inlined_at 1 1 1 .cv_loc 0 1 7 0 is_stmt 0 # :7:0 # BB#0: # %entry pushl %ebp diff --git a/test/MC/COFF/cv-inline-linetable.s b/test/MC/COFF/cv-inline-linetable.s index 67c6da2ab09..bb68fcde21b 100644 --- a/test/MC/COFF/cv-inline-linetable.s +++ b/test/MC/COFF/cv-inline-linetable.s @@ -15,6 +15,9 @@ "?baz@@YAXXZ": # @"\01?baz@@YAXXZ" Lfunc_begin0: .cv_file 1 "D:\\src\\llvm\\build\\t.cpp" + .cv_func_id 0 + .cv_inline_site_id 1 within 0 inlined_at 1 15 3 + .cv_inline_site_id 2 within 1 inlined_at 1 10 3 .cv_loc 0 1 13 0 is_stmt 0 # t.cpp:13:0 # BB#0: # %entry pushl %eax @@ -84,16 +87,18 @@ Ltmp3: Ltmp4: .short 4429 .asciz "\000\000\000\000\000\000\000\000\003\020\000" - .cv_inline_linetable 1 1 9 Lfunc_begin0 Lfunc_end0 contains 2 + .cv_inline_linetable 1 1 9 Lfunc_begin0 Lfunc_end0 # CHECK: InlineSite { # CHECK: PtrParent: 0x0 # CHECK: PtrEnd: 0x0 # CHECK: Inlinee: bar (0x1003) # CHECK: BinaryAnnotations [ -# CHECK: ChangeLineOffset: 2 -# CHECK: ChangeCodeOffset: 0x2D -# CHECK: ChangeCodeLength: 0x7 -# CHECK: ] +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x8, LineOffset: 0} +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x7, LineOffset: 1} +# CHECK-NEXT: ChangeLineOffset: 1 +# CHECK-NEXT: ChangeCodeOffset: 0x1E +# CHECK-NEXT: ChangeCodeLength: 0x7 +# CHECK-NEXT: ] # CHECK: } Ltmp5: .short Ltmp7-Ltmp6 @@ -106,12 +111,12 @@ Ltmp6: # CHECK: PtrEnd: 0x0 # CHECK: Inlinee: foo (0x1004) # CHECK: BinaryAnnotations [ -# CHECK: ChangeLineOffset: 1 -# CHECK: ChangeCodeOffset: 0x19 -# CHECK: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x6, LineOffset: 1} -# CHECK: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x7, LineOffset: 1} -# CHECK: ChangeCodeLength: 0x7 -# CHECK: ] +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0xF, LineOffset: 0} +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0xA, LineOffset: 1} +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x6, LineOffset: 1} +# CHECK-NEXT: ChangeCodeOffsetAndLineOffset: {CodeOffset: 0x7, LineOffset: 1} +# CHECK-NEXT: ChangeCodeLength: 0x7 +# CHECK-NEXT: ] # CHECK: } Ltmp7: .short 2 diff --git a/test/MC/COFF/cv-loc-cross-section.s b/test/MC/COFF/cv-loc-cross-section.s new file mode 100644 index 00000000000..357fe5b2565 --- /dev/null +++ b/test/MC/COFF/cv-loc-cross-section.s @@ -0,0 +1,26 @@ +# RUN: not llvm-mc < %s -o /dev/null 2>&1 | FileCheck %s + + .text + .global baz +baz: +.Lfunc_begin0: + .cv_file 1 "t.cpp" + .cv_func_id 0 + .cv_loc 0 1 1 1 + pushq %rbp + movq %rsp, %rbp + .cv_loc 0 1 2 1 + + .data # Switching sections raises an error. + + incl x(%rip) + .cv_loc 0 1 3 1 +# CHECK: error: all .cv_loc directives for a function must be in the same section + popq %rbp + retq +.Lfunc_end0: + + .section .debug$S,"dr" + .cv_linetable 0 .Lfunc_begin0 .Lfunc_end0 + .short 2 # Record length + .short 2 # Record kind: S_INLINESITE_END diff --git a/test/MC/COFF/cv-loc.s b/test/MC/COFF/cv-loc.s index 08ab15aea44..01d5d02b224 100644 --- a/test/MC/COFF/cv-loc.s +++ b/test/MC/COFF/cv-loc.s @@ -7,6 +7,8 @@ .cv_file 1 "a.c" .cv_file 2 "t.inc" +.cv_func_id 0 + # Implements this C: # void f(volatile int *x) { # ++*x;