Index: llvm/trunk/include/llvm/MC/MCCodeView.h =================================================================== --- llvm/trunk/include/llvm/MC/MCCodeView.h +++ llvm/trunk/include/llvm/MC/MCCodeView.h @@ -105,6 +105,55 @@ 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 @@ 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 @@ 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 @@ /// 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 Index: llvm/trunk/include/llvm/MC/MCFragment.h =================================================================== --- llvm/trunk/include/llvm/MC/MCFragment.h +++ llvm/trunk/include/llvm/MC/MCFragment.h @@ -491,7 +491,6 @@ 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 @@ 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 /// @{ Index: llvm/trunk/include/llvm/MC/MCObjectStreamer.h =================================================================== --- llvm/trunk/include/llvm/MC/MCObjectStreamer.h +++ llvm/trunk/include/llvm/MC/MCObjectStreamer.h @@ -124,13 +124,14 @@ 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; Index: llvm/trunk/include/llvm/MC/MCStreamer.h =================================================================== --- llvm/trunk/include/llvm/MC/MCStreamer.h +++ llvm/trunk/include/llvm/MC/MCStreamer.h @@ -713,11 +713,20 @@ /// 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 @@ /// \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. Index: llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -124,7 +124,16 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 Index: llvm/trunk/lib/MC/MCAsmStreamer.cpp =================================================================== --- llvm/trunk/lib/MC/MCAsmStreamer.cpp +++ llvm/trunk/lib/MC/MCAsmStreamer.cpp @@ -222,15 +222,20 @@ 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 @@ 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 @@ 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 @@ 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( Index: llvm/trunk/lib/MC/MCCodeView.cpp =================================================================== --- llvm/trunk/lib/MC/MCCodeView.cpp +++ llvm/trunk/lib/MC/MCCodeView.cpp @@ -65,6 +65,50 @@ 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 @@ 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 @@ 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 @@ 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); Index: llvm/trunk/lib/MC/MCObjectStreamer.cpp =================================================================== --- llvm/trunk/lib/MC/MCObjectStreamer.cpp +++ llvm/trunk/lib/MC/MCObjectStreamer.cpp @@ -369,13 +369,13 @@ 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::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( Index: llvm/trunk/lib/MC/MCParser/AsmParser.cpp =================================================================== --- llvm/trunk/lib/MC/MCParser/AsmParser.cpp +++ llvm/trunk/lib/MC/MCParser/AsmParser.cpp @@ -378,6 +378,9 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /// 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 @@ Lex(); getStreamer().EmitCVLocDirective(FunctionId, FileNumber, LineNumber, - ColumnPos, PrologueEnd, IsStmt, StringRef()); + ColumnPos, PrologueEnd, IsStmt, StringRef(), + DirectiveLoc); return false; } @@ -3317,10 +3422,7 @@ 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 @@ /// 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 @@ "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 @@ MCSymbol *FnEndSym = getContext().getOrCreateSymbol(FnEndName); getStreamer().EmitCVInlineLinetableDirective(PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, - FnEndSym, SecondaryFunctionIds); + FnEndSym); return false; } @@ -4701,9 +4780,11 @@ 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; Index: llvm/trunk/lib/MC/MCStreamer.cpp =================================================================== --- llvm/trunk/lib/MC/MCStreamer.cpp +++ llvm/trunk/lib/MC/MCStreamer.cpp @@ -220,22 +220,54 @@ 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, Index: llvm/trunk/test/DebugInfo/COFF/inlining-header.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/inlining-header.ll +++ llvm/trunk/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: } Index: llvm/trunk/test/DebugInfo/COFF/inlining-levels.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/inlining-levels.ll +++ llvm/trunk/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: } Index: llvm/trunk/test/DebugInfo/COFF/inlining.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/inlining.ll +++ llvm/trunk/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: } Index: llvm/trunk/test/DebugInfo/COFF/local-variables.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/local-variables.ll +++ llvm/trunk/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 Index: llvm/trunk/test/MC/COFF/cv-def-range.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-def-range.s +++ llvm/trunk/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 Index: llvm/trunk/test/MC/COFF/cv-empty-linetable.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-empty-linetable.s +++ llvm/trunk/test/MC/COFF/cv-empty-linetable.s @@ -16,6 +16,7 @@ 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: Index: llvm/trunk/test/MC/COFF/cv-errors.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-errors.s +++ llvm/trunk/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 Index: llvm/trunk/test/MC/COFF/cv-inline-linetable-infloop.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-inline-linetable-infloop.s +++ llvm/trunk/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 @@ .globl afterinfloop .p2align 4, 0x90 afterinfloop: # @afterinfloop + .cv_func_id 3 .cv_loc 3 1 13 0 # t.c:13:0 retq Index: llvm/trunk/test/MC/COFF/cv-inline-linetable-unlikely.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-inline-linetable-unlikely.s +++ llvm/trunk/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 + Index: llvm/trunk/test/MC/COFF/cv-inline-linetable-unreachable.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-inline-linetable-unreachable.s +++ llvm/trunk/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 Index: llvm/trunk/test/MC/COFF/cv-inline-linetable.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-inline-linetable.s +++ llvm/trunk/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 @@ 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 @@ # 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 Index: llvm/trunk/test/MC/COFF/cv-loc-cross-section.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-loc-cross-section.s +++ llvm/trunk/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 Index: llvm/trunk/test/MC/COFF/cv-loc.s =================================================================== --- llvm/trunk/test/MC/COFF/cv-loc.s +++ llvm/trunk/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;