Index: include/llvm/ADT/DenseMapInfo.h =================================================================== --- include/llvm/ADT/DenseMapInfo.h +++ include/llvm/ADT/DenseMapInfo.h @@ -14,6 +14,8 @@ #ifndef LLVM_ADT_DENSEMAPINFO_H #define LLVM_ADT_DENSEMAPINFO_H +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/type_traits.h" @@ -129,6 +131,17 @@ } }; +template<> struct DenseMapInfo { + static inline StringRef getEmptyKey() { return StringRef("emptykey"); } + static inline StringRef getTombstoneKey() { return StringRef("tombstone!"); } + static unsigned getHashValue(const StringRef &Val) { + return (unsigned)hash_value(hash_value(Val)); + } + static bool isEqual(const StringRef &LHS, const StringRef &RHS) { + return LHS == RHS; + } +}; + // Provide DenseMapInfo for all pairs whose members have info. template struct DenseMapInfo > { Index: include/llvm/MC/MCAssembler.h =================================================================== --- include/llvm/MC/MCAssembler.h +++ include/llvm/MC/MCAssembler.h @@ -54,6 +54,7 @@ FT_Org, FT_Dwarf, FT_DwarfFrame, + FT_CoffLabelDiff, FT_LEB }; @@ -538,6 +539,35 @@ } }; +class MCCoffLabelDiffFragment : public MCFragment { + virtual void anchor(); + + /// AddrDelta - The expression for the difference of the two labels. + const MCExpr *AddrDelta; + + SmallString<8> Contents; + +public: + MCCoffLabelDiffFragment(const MCExpr &AddrDelta, MCSectionData *SD = 0) + : MCFragment(FT_CoffLabelDiff, SD), AddrDelta(&AddrDelta) { + Contents.assign(4u, '\0'); + } + + /// @name Accessors + /// @{ + + const MCExpr &getAddrDelta() const { return *AddrDelta; } + + SmallString<8> &getContents() { return Contents; } + const SmallString<8> &getContents() const { return Contents; } + + /// @} + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_CoffLabelDiff; + } +}; + // FIXME: Should this be a separate class, or just merged into MCSection? Since // we anticipate the fast path being through an MCAssembler, the only reason to // keep it out is for API abstraction. @@ -932,6 +962,8 @@ bool relaxDwarfLineAddr(MCAsmLayout &Layout, MCDwarfLineAddrFragment &DF); bool relaxDwarfCallFrameFragment(MCAsmLayout &Layout, MCDwarfCallFrameFragment &DF); + bool relaxCoffLabelDiffFragment(MCAsmLayout &Layout, + MCCoffLabelDiffFragment &LDF); /// finishLayout - Finalize a layout, including fragment lowering. void finishLayout(MCAsmLayout &Layout); Index: include/llvm/MC/MCObjectFileInfo.h =================================================================== --- include/llvm/MC/MCObjectFileInfo.h +++ include/llvm/MC/MCObjectFileInfo.h @@ -129,6 +129,8 @@ const MCSection *DwarfGnuPubNamesSection; const MCSection *DwarfGnuPubTypesSection; + const MCSection *COFFDebugInfo; + // Extra TLS Variable Data section. If the target needs to put additional // information for a TLS variable, it'll go here. const MCSection *TLSExtraDataSection; @@ -281,6 +283,10 @@ return DwarfAddrSection; } + const MCSection *getCOFFDebugInfo() const { + return COFFDebugInfo; + } + const MCSection *getTLSExtraDataSection() const { return TLSExtraDataSection; } Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -410,11 +410,20 @@ /// EndCOFFSymbolDef - Marks the end of the symbol definition. virtual void EndCOFFSymbolDef() = 0; + /// EmitCOFFSectionReference - Emits a COFF section number. + /// + /// @param Symbol - Symbol the section relative realocation should point to. + virtual void EmitCOFFSectionReference(MCSymbol const *Symbol); + /// EmitCOFFSecRel32 - Emits a COFF section relative relocation. /// /// @param Symbol - Symbol the section relative realocation should point to. virtual void EmitCOFFSecRel32(MCSymbol const *Symbol); + /// EmitCOFFStaticOffset - Emits statically-known difference between two + /// labels in the same section. + virtual void EmitCOFFStaticOffset(const MCSymbol *From, const MCSymbol *To); + /// EmitELFSize - Emit an ELF .size directive. /// /// This corresponds to an assembler statement such as: Index: include/llvm/Support/DebugLoc.h =================================================================== --- include/llvm/Support/DebugLoc.h +++ include/llvm/Support/DebugLoc.h @@ -89,6 +89,8 @@ void getScopeAndInlinedAt(MDNode *&Scope, MDNode *&IA, const LLVMContext &Ctx) const; + /// getScopeNode - Get MDNode for DebugLoc's scope, or null if invalid. + MDNode *getScopeNode(const LLVMContext &Ctx) const; /// getAsMDNode - This method converts the compressed DebugLoc node into a /// DILocation compatible MDNode. Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -49,11 +49,13 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/Target/TargetRegisterInfo.h" #include "llvm/Transforms/Utils/GlobalStatus.h" +#include "WinCodeViewLineTables.h" using namespace llvm; static const char *const DWARFGroupName = "DWARF Emission"; -static const char *const DbgTimerName = "DWARF Debug Writer"; +static const char *const DbgTimerName = "Debug Info Emission"; static const char *const EHTimerName = "DWARF Exception Writer"; +static const char *const CodeViewLineTablesGroupName = "CodeView Line Tables"; STATISTIC(EmittedInsts, "Number of machine instrs printed"); @@ -193,8 +195,14 @@ } if (MAI->doesSupportDebugInformation()) { - DD = new DwarfDebug(this, &M); - Handlers.push_back(HandlerInfo(DD, DbgTimerName, DWARFGroupName)); + if (Triple(TM.getTargetTriple()).isOSWindows()) { + Handlers.push_back(HandlerInfo(new WinCodeViewLineTables(this), + DbgTimerName, + CodeViewLineTablesGroupName)); + } else { + DD = new DwarfDebug(this, &M); + Handlers.push_back(HandlerInfo(DD, DbgTimerName, DWARFGroupName)); + } } DwarfException *DE = 0; Index: lib/CodeGen/AsmPrinter/CMakeLists.txt =================================================================== --- lib/CodeGen/AsmPrinter/CMakeLists.txt +++ lib/CodeGen/AsmPrinter/CMakeLists.txt @@ -13,6 +13,7 @@ ErlangGCPrinter.cpp OcamlGCPrinter.cpp Win64Exception.cpp + WinCodeViewLineTables.cpp ) add_dependencies(LLVMAsmPrinter intrinsics_gen) Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1545,17 +1545,10 @@ } } -// Get MDNode for DebugLoc's scope. -static MDNode *getScopeNode(DebugLoc DL, const LLVMContext &Ctx) { - if (MDNode *InlinedAt = DL.getInlinedAt(Ctx)) - return getScopeNode(DebugLoc::getFromDILocation(InlinedAt), Ctx); - return DL.getScope(Ctx); -} - // Walk up the scope chain of given debug loc and find line number info // for the function. static DebugLoc getFnDebugLoc(DebugLoc DL, const LLVMContext &Ctx) { - const MDNode *Scope = getScopeNode(DL, Ctx); + const MDNode *Scope = DL.getScopeNode(Ctx); DISubprogram SP = getDISubprogram(Scope); if (SP.isSubprogram()) { // Check for number of operands since the compatibility is Index: lib/CodeGen/AsmPrinter/WinCodeViewLineTables.h =================================================================== --- /dev/null +++ lib/CodeGen/AsmPrinter/WinCodeViewLineTables.h @@ -0,0 +1,131 @@ +//===-- llvm/lib/CodeGen/AsmPrinter/WinCodeViewLineTables.h ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing line tables info into COFF files. +// +//===----------------------------------------------------------------------===// + +#ifndef CODEGEN_ASMPRINTER_WINCODEVIEWLINETABLES_H__ +#define CODEGEN_ASMPRINTER_WINCODEVIEWLINETABLES_H__ + +#include "AsmPrinterHandler.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/LexicalScopes.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/DebugLoc.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + +namespace llvm { +/// \brief Collects and handles line tables information in a CodeView format. +class WinCodeViewLineTables : public AsmPrinterHandler { + AsmPrinter *Asm; + DebugLoc PrevInstLoc; + LexicalScopes LScopes; + + struct FunctionDI { + SmallVector Instrs; + MCSymbol *End; + } *CurFn; + + typedef DenseMap FnDebugInfoTy; + FnDebugInfoTy FnDebugInfo; + // Store the functions we've visited in a vector so we can maintain a stable + // order while emitting subsections. + SmallVector VisitedFunctions; + + struct InstrInfoTy { + StringRef Filename; + unsigned LineNumber; + + InstrInfoTy() : LineNumber(0) {} + + InstrInfoTy(StringRef Filename, unsigned LineNumber) + : Filename(Filename), LineNumber(LineNumber) {} + }; + DenseMap InstrInfo; + + struct FileNameRegistryTy { + SmallVector Filenames; + struct PerFileInfo { + size_t FilenameID, StartOffset; + }; + StringMap Infos; + size_t LastOffset; + + FileNameRegistryTy() { + clear(); + } + + void add(StringRef Filename) { + if (Infos.count(Filename)) + return; + size_t OldSize = Infos.size(); + Infos[Filename].FilenameID = OldSize; + Infos[Filename].StartOffset = LastOffset; + LastOffset += Filename.size() + 1; + Filenames.push_back(Filename); + } + + void clear() { + LastOffset = 1; + Infos.clear(); + Filenames.clear(); + } + } FileNameRegistry; + + typedef DenseMap, char *> + DirAndFilenameToFilepathMapTy; + DirAndFilenameToFilepathMapTy DirAndFilenameToFilepathMap; + StringRef getFullFilepath(const MDNode *S); + + void maybeRecordLocation(DebugLoc DL, const MachineFunction *MF); + + void clear() { + assert(CurFn == 0); + FileNameRegistry.clear(); + InstrInfo.clear(); + } + +public: + WinCodeViewLineTables(AsmPrinter *Asm); + + ~WinCodeViewLineTables() { + for (DirAndFilenameToFilepathMapTy::iterator + I = DirAndFilenameToFilepathMap.begin(), + E = DirAndFilenameToFilepathMap.end(); + I != E; ++I) + free(I->second); + } + + virtual void setSymbolSize(const llvm::MCSymbol *, uint64_t) {} + + /// \brief Emit the COFF section that holds the line table information. + virtual void endModule(); + + /// \brief Gather pre-function debug information. + virtual void beginFunction(const MachineFunction *MF); + + /// \brief Gather post-function debug information. + virtual void endFunction(const MachineFunction *); + + /// \brief Process beginning of an instruction. + virtual void beginInstruction(const MachineInstr *MI); + + /// \brief Process end of an instruction. + virtual void endInstruction() {} +}; +} // End of namespace llvm + +#endif Index: lib/CodeGen/AsmPrinter/WinCodeViewLineTables.cpp =================================================================== --- /dev/null +++ lib/CodeGen/AsmPrinter/WinCodeViewLineTables.cpp @@ -0,0 +1,323 @@ +//===-- llvm/lib/CodeGen/AsmPrinter/WinCodeViewLineTables.cpp --*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing line tables info into COFF files. +// +//===----------------------------------------------------------------------===// + +#include "WinCodeViewLineTables.h" +#include "llvm/MC/MCSymbol.h" + +namespace llvm { + +StringRef WinCodeViewLineTables::getFullFilepath(const MDNode *S) { + assert(S); + DIDescriptor D(S); + assert((D.isCompileUnit() || D.isFile() || D.isSubprogram() || + D.isLexicalBlockFile() || D.isLexicalBlock()) && + "Unexpected scope info"); + + DIScope Scope(S); + StringRef Dir = Scope.getDirectory(), + Filename = Scope.getFilename(); + char *&Result = DirAndFilenameToFilepathMap[std::make_pair(Dir, Filename)]; + if (Result != 0) + return Result; + + // Clang emits directory and relative filename info into the IR, but CodeView + // operates on full paths. We could change Clang to emit full paths too, but + // that would increase the IR size and probably not needed for other users. + // For now, just concatenate and canonicalize the path here. + std::string Filepath; + if (Filename.find(':') == 1) + Filepath = Filename; + else + Filepath = (Dir + Twine("\\") + Filename).str(); + + // Canonicalize the path. We have to do it textually because we may no longer + // have access the file in the filesystem. + // First, replace all slashes with backslashes. + std::replace(Filepath.begin(), Filepath.end(), '/', '\\'); + + // Remove all "\.\" with "\". + size_t Cursor = 0; + while ((Cursor = Filepath.find("\\.\\", Cursor)) != std::string::npos) + Filepath.erase(Cursor, 2); + + // Replace all "\XXX\..\" with "\". Don't try too hard though as the original + // path should be well-formatted, e.g. start with a drive letter, etc. + Cursor = 0; + while ((Cursor = Filepath.find("\\..\\", Cursor)) != std::string::npos) { + // Something's wrong if the path starts with "\..\", abort. + if (Cursor == 0) + break; + + size_t PrevSlash = Filepath.rfind('\\', Cursor - 1); + if (PrevSlash == std::string::npos) + // Something's wrong, abort. + break; + + Filepath.erase(PrevSlash, Cursor + 3 - PrevSlash); + // The next ".." might be following the one we've just erased. + Cursor = PrevSlash; + } + + // Remove all duplicate backslashes. + Cursor = 0; + while ((Cursor = Filepath.find("\\\\", Cursor)) != std::string::npos) + Filepath.erase(Cursor, 1); + + Result = strdup(Filepath.c_str()); + return StringRef(Result); +} + +void WinCodeViewLineTables::maybeRecordLocation(DebugLoc DL, + const MachineFunction *MF) { + const MDNode *Scope = DL.getScope(MF->getFunction()->getContext()); + if (!Scope) + return; + StringRef Filename = getFullFilepath(Scope); + + // Skip this instruction if it has the same file:line as the previous one. + assert(CurFn); + if (!CurFn->Instrs.empty()) { + const InstrInfoTy &LastInstr = InstrInfo[CurFn->Instrs.back()]; + if (LastInstr.Filename == Filename && LastInstr.LineNumber == DL.getLine()) + return; + } + FileNameRegistry.add(Filename); + + MCSymbol *MCL = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitLabel(MCL); + CurFn->Instrs.push_back(MCL); + InstrInfo[MCL] = InstrInfoTy(Filename, DL.getLine()); +} + +WinCodeViewLineTables::WinCodeViewLineTables(AsmPrinter *Asm) + : Asm(Asm), CurFn(0) { + // Tell MMI that we have debug info. + Asm->MMI->setDebugInfoAvailability(true); +} + +void WinCodeViewLineTables::endModule() { + if (FnDebugInfo.empty()) + return; + + Asm->OutStreamer.SwitchSection(Asm->getObjFileLowering().getCOFFDebugInfo()); + + Asm->EmitInt32(4); // Starting magic number. + + // The COFF .debug$S section consists of several subsections, each starting + // with a 4-byte control code (e.g. 0xF1, 0xF2, etc) and then a 4-byte length + // of the payload followed by the payload itself. The subsections are 4-byte + // aligned. + + // For each function there is a separate F2 subsection + // which holds the PC to file:line table. + for (size_t I = 0, E = VisitedFunctions.size(); I != E; ++I) { + const Function *GV = VisitedFunctions[I]; + const MCSymbol *Fn = Asm->getSymbol(GV); + const FunctionDI &FDI = FnDebugInfo[GV]; + assert(Fn); + assert(FDI.Instrs.size() > 0); + + // PC are grouped into segments sharing the same filename. + // Pre-calculate the lengths of these segments for convenience. + DenseMap FilenameSegmentLengths; + size_t LastSegmentEnd = 0; + StringRef PrevFilename = InstrInfo[FDI.Instrs[0]].Filename; + for (size_t J = 1, F = FDI.Instrs.size(); J != F; ++J) { + if (PrevFilename == InstrInfo[FDI.Instrs[J]].Filename) + continue; + FilenameSegmentLengths[LastSegmentEnd] = J - LastSegmentEnd; + LastSegmentEnd = J; + PrevFilename = InstrInfo[FDI.Instrs[J]].Filename; + } + FilenameSegmentLengths[LastSegmentEnd] = FDI.Instrs.size() - LastSegmentEnd; + + // Emit the control code of the subsection followed by the payload size. + Asm->OutStreamer.AddComment( + "Linetable subsection for " + Twine(Fn->getName())); + Asm->EmitInt32(0xF2); + MCSymbol *SubsectionBegin = Asm->MMI->getContext().CreateTempSymbol(), + *SubsectionEnd = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitCOFFStaticOffset(SubsectionBegin, SubsectionEnd); + Asm->OutStreamer.EmitLabel(SubsectionBegin); + + // Identify the function this subsection is for. + Asm->OutStreamer.EmitCOFFSecRel32(Fn); + Asm->OutStreamer.EmitCOFFSectionReference(Fn); + + // Length of the function's code, in bytes. + Asm->OutStreamer.EmitCOFFStaticOffset(Fn, FDI.End); + + // PC-to-linenumber lookup table: + MCSymbol *FileSegmentEnd = 0; + for (size_t J = 0, F = FDI.Instrs.size(); J != F; ++J) { + MCSymbol *Instr = FDI.Instrs[J]; + assert(InstrInfo.count(Instr)); + + if (FilenameSegmentLengths.count(J)) { + if (FileSegmentEnd) + Asm->OutStreamer.EmitLabel(FileSegmentEnd); + StringRef CurFilename = InstrInfo[FDI.Instrs[J]].Filename; + assert(FileNameRegistry.Infos.count(CurFilename)); + size_t IDInF3 = FileNameRegistry.Infos[CurFilename].FilenameID; + // Each segment starts with the offset of the filename in F3. + Asm->OutStreamer.AddComment( + "Segment for file '" + Twine(CurFilename) + "' begins"); + MCSymbol *FileSegmentBegin = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitLabel(FileSegmentBegin); + Asm->EmitInt32(8 * IDInF3); + + // Number of PC records in the lookup table. + size_t SegmentLength = FilenameSegmentLengths[J]; + Asm->EmitInt32(SegmentLength); + + // Full size of the segment for this filename, including the prev two + // records. + FileSegmentEnd = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitCOFFStaticOffset(FileSegmentBegin, FileSegmentEnd); + } + + // The first PC with the given linenumber and the linenumber itself. + Asm->OutStreamer.EmitCOFFStaticOffset(Fn, Instr); + Asm->EmitInt32(InstrInfo[Instr].LineNumber); + } + if (FileSegmentEnd) + Asm->OutStreamer.EmitLabel(FileSegmentEnd); + Asm->OutStreamer.EmitLabel(SubsectionEnd); + } + + // A F4 subsection holds a file index to offset in string table table. + Asm->OutStreamer.AddComment("File index to string table offset subsection"); + Asm->EmitInt32(0xF4); + size_t NumFilenames = FileNameRegistry.Infos.size(); + Asm->EmitInt32(8 * NumFilenames); + for (size_t I = 0, E = FileNameRegistry.Filenames.size(); I != E; ++I) { + StringRef Filename = FileNameRegistry.Filenames[I]; + // For each unique filename, just write it's offset in the string table. + Asm->EmitInt32(FileNameRegistry.Infos[Filename].StartOffset); + // The function name offset is not followed by any additional data. + Asm->EmitInt32(0); + } + + // A F3 subsection holds the string table. + Asm->OutStreamer.AddComment("String table"); + Asm->EmitInt32(0xF3); + Asm->EmitInt32(FileNameRegistry.LastOffset); + // The payload starts with a null character. + Asm->EmitInt8(0); + + for (size_t I = 0, E = FileNameRegistry.Filenames.size(); I != E; ++I) { + // Just emit unique filenames one by one, separated by a null character. + Asm->OutStreamer.EmitBytes(FileNameRegistry.Filenames[I]); + Asm->EmitInt8(0); + } + + // No more subsections. Fill with zeros to align the end of the section by 4. + Asm->OutStreamer.EmitFill((-FileNameRegistry.LastOffset) % 4, 0); + + clear(); +} + +// TODO: This function was copied from DwardDebug.cpp. Any reason not to move +// the code to DebugLoc.h? +// Walk up the scope chain of given debug loc and find line number info +// for the function. +static DebugLoc getFnDebugLoc(DebugLoc DL, const LLVMContext &Ctx) { + const MDNode *Scope = DL.getScopeNode(Ctx); + DISubprogram SP = getDISubprogram(Scope); + if (SP.isSubprogram()) { + // Check for number of operands since the compatibility is + // cheap here. + // TODO: What's the 19 magic number? Is it a DWARF limitation or the way to + // distinguish between what and what? + if (SP->getNumOperands() > 19) + return DebugLoc::get(SP.getScopeLineNumber(), 0, SP); + else + return DebugLoc::get(SP.getLineNumber(), 0, SP); + } + + return DebugLoc(); +} + +void WinCodeViewLineTables::beginFunction(const MachineFunction *MF) { + assert(!CurFn && "Can't process two functions at once!"); + + if (!Asm->MMI->hasDebugInfo()) + return; + + // Grab the lexical scopes for the function, if we don't have any of those + // then we're not going to be able to do anything. + LScopes.initialize(*MF); + if (LScopes.empty()) + return; + + const Function *GV = MF->getFunction(); + assert(FnDebugInfo.count(GV) == false); + VisitedFunctions.push_back(GV); + CurFn = &FnDebugInfo[GV]; + + // Find the end of the function prolog. + // TODO: is there a simpler a way to do this? Can we just search for the first + // instruction of the function, not the last of the prolog? + DebugLoc PrologEndLoc; + bool EmptyPrologue = true; + for (MachineFunction::const_iterator I = MF->begin(), E = MF->end(); + I != E && PrologEndLoc.isUnknown(); ++I) { + for (MachineBasicBlock::const_iterator II = I->begin(), IE = I->end(); + II != IE; ++II) { + const MachineInstr *MI = II; + if (MI->isDebugValue()) + continue; + + // First known non-DBG_VALUE and non-frame setup location marks + // the beginning of the function body. + // TODO: do I need the first subcondition? + if (!MI->getFlag(MachineInstr::FrameSetup) && + (!MI->getDebugLoc().isUnknown())) { + PrologEndLoc = MI->getDebugLoc(); + break; + } + EmptyPrologue = false; + } + } + // Record beginning of function if we have a non-empty prologue. + if (!PrologEndLoc.isUnknown() && !EmptyPrologue) { + DebugLoc FnStartDL = + getFnDebugLoc(PrologEndLoc, MF->getFunction()->getContext()); + maybeRecordLocation(FnStartDL, MF); + } +} + +void WinCodeViewLineTables::endFunction(const MachineFunction *) { + if (!CurFn) // We haven't created any debug info for this function. + return; + + if (CurFn->Instrs.empty()) + llvm_unreachable("Can this ever happen?"); + + // Define end label for subprogram. + MCSymbol *FunctionEndSym = Asm->OutStreamer.getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitLabel(FunctionEndSym); + CurFn->End = FunctionEndSym; + CurFn = 0; +} + +void WinCodeViewLineTables::beginInstruction(const MachineInstr *MI) { + // Ignore DBG_VALUE locations and function prologue. + if (MI->isDebugValue() || MI->getFlag(MachineInstr::FrameSetup)) + return; + DebugLoc DL = MI->getDebugLoc(); + if (DL == PrevInstLoc || DL.isUnknown()) + return; + maybeRecordLocation(DL, Asm->MF); +} +} Index: lib/IR/DebugLoc.cpp =================================================================== --- lib/IR/DebugLoc.cpp +++ lib/IR/DebugLoc.cpp @@ -70,6 +70,11 @@ IA = Ctx.pImpl->ScopeInlinedAtRecords[-ScopeIdx-1].second.get(); } +MDNode *DebugLoc::getScopeNode(const LLVMContext &Ctx) const { + if (MDNode *InlinedAt = getInlinedAt(Ctx)) + return DebugLoc::getFromDILocation(InlinedAt).getScopeNode(Ctx); + return getScope(Ctx); +} DebugLoc DebugLoc::get(unsigned Line, unsigned Col, MDNode *Scope, MDNode *InlinedAt) { Index: lib/MC/MCAsmStreamer.cpp =================================================================== --- lib/MC/MCAsmStreamer.cpp +++ lib/MC/MCAsmStreamer.cpp @@ -160,7 +160,9 @@ virtual void EmitCOFFSymbolStorageClass(int StorageClass); virtual void EmitCOFFSymbolType(int Type); virtual void EndCOFFSymbolDef(); + virtual void EmitCOFFSectionReference(MCSymbol const *Symbol); virtual void EmitCOFFSecRel32(MCSymbol const *Symbol); + virtual void EmitCOFFStaticOffset(const MCSymbol *From, const MCSymbol *To); virtual void EmitELFSize(MCSymbol *Symbol, const MCExpr *Value); virtual void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment); @@ -505,8 +507,19 @@ EmitEOL(); } +void MCAsmStreamer::EmitCOFFSectionReference(MCSymbol const *Symbol) { + OS << "\t.secref\t" << *Symbol; + EmitEOL(); +} + void MCAsmStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol) { - OS << "\t.secrel32\t" << *Symbol << '\n'; + OS << "\t.secrel32\t" << *Symbol; + EmitEOL(); +} + +void MCAsmStreamer::EmitCOFFStaticOffset(const MCSymbol *From, + const MCSymbol *To) { + OS << "\t.offset from " << *From << " to " << *To; EmitEOL(); } Index: lib/MC/MCAssembler.cpp =================================================================== --- lib/MC/MCAssembler.cpp +++ lib/MC/MCAssembler.cpp @@ -469,6 +469,8 @@ return cast(F).getContents().size(); case MCFragment::FT_DwarfFrame: return cast(F).getContents().size(); + case MCFragment::FT_CoffLabelDiff: + return cast(F).getContents().size(); } llvm_unreachable("invalid fragment kind"); @@ -676,6 +678,11 @@ OW->WriteBytes(CF.getContents().str()); break; } + case MCFragment::FT_CoffLabelDiff: { + const MCCoffLabelDiffFragment &LDF = cast(F); + OW->WriteBytes(LDF.getContents().str()); + break; + } } assert(OW->getStream().tell() - Start == FragmentSize && @@ -937,6 +944,25 @@ return OldSize != Data.size(); } +bool MCAssembler::relaxCoffLabelDiffFragment(MCAsmLayout &Layout, + MCCoffLabelDiffFragment &LDF) { + MCContext &Context = Layout.getAssembler().getContext(); + int64_t AddrDelta = 0; + uint64_t OldSize = LDF.getContents().size(); + bool IsAbs = LDF.getAddrDelta().EvaluateAsAbsolute(AddrDelta, Layout); + (void)IsAbs; + assert(IsAbs); + SmallString<8> &Data = LDF.getContents(); + Data.clear(); + raw_svector_ostream OSE(Data); + OSE << uint8_t( AddrDelta & 0xff); + OSE << uint8_t((AddrDelta >> 8) & 0xff); + OSE << uint8_t((AddrDelta >> 16) & 0xff); + OSE << uint8_t((AddrDelta >> 24) & 0xff); + OSE.flush(); + return OldSize != Data.size(); +} + bool MCAssembler::layoutSectionOnce(MCAsmLayout &Layout, MCSectionData &SD) { // Holds the first fragment which needed relaxing during this layout. It will // remain NULL if none were relaxed. @@ -965,6 +991,10 @@ relaxDwarfCallFrameFragment(Layout, *cast(I)); break; + case MCFragment::FT_CoffLabelDiff: + RelaxedFrag = + relaxCoffLabelDiffFragment(Layout, *cast(I)); + break; case MCFragment::FT_LEB: RelaxedFrag = relaxLEB(Layout, *cast(I)); break; @@ -1027,6 +1057,7 @@ case MCFragment::FT_Org: OS << "MCOrgFragment"; break; case MCFragment::FT_Dwarf: OS << "MCDwarfFragment"; break; case MCFragment::FT_DwarfFrame: OS << "MCDwarfCallFrameFragment"; break; + case MCFragment::FT_CoffLabelDiff: OS << "MCCoffLabelDiffFragment"; break; case MCFragment::FT_LEB: OS << "MCLEBFragment"; break; } @@ -1114,6 +1145,12 @@ OS << " AddrDelta:" << CF->getAddrDelta(); break; } + case MCFragment::FT_CoffLabelDiff: { + const MCCoffLabelDiffFragment *LDF = cast(this); + OS << "\n "; + OS << " AddrDelta:" << LDF->getAddrDelta(); + break; + } case MCFragment::FT_LEB: { const MCLEBFragment *LF = cast(this); OS << "\n "; @@ -1185,3 +1222,4 @@ void MCLEBFragment::anchor() { } void MCDwarfLineAddrFragment::anchor() { } void MCDwarfCallFrameFragment::anchor() { } +void MCCoffLabelDiffFragment::anchor() { } \ No newline at end of file Index: lib/MC/MCObjectFileInfo.cpp =================================================================== --- lib/MC/MCObjectFileInfo.cpp +++ lib/MC/MCObjectFileInfo.cpp @@ -581,6 +581,13 @@ SectionKind::getReadOnly()); // Debug info. + COFFDebugInfo = + Ctx->getCOFFSection(".debug$S", + COFF::IMAGE_SCN_MEM_DISCARDABLE | + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ, + SectionKind::getMetadata()); + DwarfAbbrevSection = Ctx->getCOFFSection(".debug_abbrev", COFF::IMAGE_SCN_MEM_DISCARDABLE | Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -566,10 +566,19 @@ EmitLabel(CurFrame->PrologEnd); } +void MCStreamer::EmitCOFFSectionReference(MCSymbol const *Symbol) { + llvm_unreachable("This file format doesn't support this directive"); +} + void MCStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol) { llvm_unreachable("This file format doesn't support this directive"); } +void MCStreamer::EmitCOFFStaticOffset(const MCSymbol *From, + const MCSymbol *To) { + llvm_unreachable("This file format doesn't support this directive"); +} + /// EmitRawText - If this file is backed by an assembly streamer, this dumps /// the specified string in the output .s file. This capability is /// indicated by the hasRawTextSupport() predicate. Index: lib/MC/WinCOFFStreamer.cpp =================================================================== --- lib/MC/WinCOFFStreamer.cpp +++ lib/MC/WinCOFFStreamer.cpp @@ -61,7 +61,9 @@ virtual void EmitCOFFSymbolStorageClass(int StorageClass); virtual void EmitCOFFSymbolType(int Type); virtual void EndCOFFSymbolDef(); + virtual void EmitCOFFSectionReference(MCSymbol const *Symbol); virtual void EmitCOFFSecRel32(MCSymbol const *Symbol); + virtual void EmitCOFFStaticOffset(const MCSymbol *From, const MCSymbol *To); virtual void EmitELFSize(MCSymbol *Symbol, const MCExpr *Value); virtual void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment); @@ -249,6 +251,17 @@ CurSymbol = NULL; } +void WinCOFFStreamer::EmitCOFFSectionReference(MCSymbol const *Symbol) +{ + MCDataFragment *DF = getOrCreateDataFragment(); + + DF->getFixups().push_back( + MCFixup::Create(DF->getContents().size(), + MCSymbolRefExpr::Create (Symbol, getContext ()), + FK_SecRel_2)); + DF->getContents().resize(DF->getContents().size() + 4, 0); +} + void WinCOFFStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol) { MCDataFragment *DF = getOrCreateDataFragment(); @@ -260,6 +273,18 @@ DF->getContents().resize(DF->getContents().size() + 4, 0); } +void WinCOFFStreamer::EmitCOFFStaticOffset(const MCSymbol *From, + const MCSymbol *To) { + const MCExpr *AddrDelta = BuildSymbolDiff(getContext(), To, From); + int64_t Res; + if (AddrDelta->EvaluateAsAbsolute(Res, getAssembler())) { + EmitIntValue(Res, 4); + return; + } + AddrDelta = ForceExpAbs(AddrDelta); + insert(new MCCoffLabelDiffFragment(*AddrDelta)); +} + void WinCOFFStreamer::EmitELFSize(MCSymbol *Symbol, const MCExpr *Value) { llvm_unreachable("not implemented"); } Index: lib/Target/X86/MCTargetDesc/X86WinCOFFObjectWriter.cpp =================================================================== --- lib/Target/X86/MCTargetDesc/X86WinCOFFObjectWriter.cpp +++ lib/Target/X86/MCTargetDesc/X86WinCOFFObjectWriter.cpp @@ -65,6 +65,9 @@ if (Is64Bit) return COFF::IMAGE_REL_AMD64_ADDR64; llvm_unreachable("unsupported relocation type"); + case FK_SecRel_2: + return Is64Bit ? COFF::IMAGE_REL_AMD64_SECTION + : COFF::IMAGE_REL_I386_SECTION; case FK_SecRel_4: return Is64Bit ? COFF::IMAGE_REL_AMD64_SECREL : COFF::IMAGE_REL_I386_SECREL; default: Index: test/DebugInfo/X86/CodeView/asm.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/asm.ll @@ -0,0 +1,90 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ32 %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ64 %s + +; This LL file was generated by running clang on the following code: +; D:\asm.c: +; 1 void g(void); +; 2 +; 3 void f(void) { +; 4 __asm align 16; +; 5 g(); +; 6 } + +; OBJ32: Section { +; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ32: Characteristics [ (0x42100040) +; OBJ32: ] +; OBJ32: Relocations [ +; OBJ32-NEXT: 0xC IMAGE_REL_I386_SECREL _f +; OBJ32-NEXT: 0x10 IMAGE_REL_I386_SECTION _f +; OBJ32-NEXT: ] +; OBJ32: FunctionLineTable [ +; OBJ32-NEXT: Name: _f +; OBJ32-NEXT: CodeSize: 0x6 +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\asm.c +; FIXME: An empty __asm stmt creates an extra entry. +; We seem to know that these offsets are the same statically during the +; execution of endModule(). +; OBJ32-NEXT: +0x0: 4 +; OBJ32-NEXT: +0x0: 5 +; OBJ32-NEXT: +0x5: 6 +; OBJ32-NEXT: ] +; OBJ32-NEXT: ] +; OBJ32: } + +; OBJ64: Section { +; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ64: Characteristics [ (0x42100040) +; OBJ64: ] +; OBJ64: Relocations [ +; OBJ64-NEXT: 0xC IMAGE_REL_AMD64_SECREL f +; OBJ64-NEXT: 0x10 IMAGE_REL_AMD64_SECTION f +; OBJ64-NEXT: ] +; OBJ64: FunctionLineTable [ +; OBJ64-NEXT: Name: f +; OBJ64-NEXT: CodeSize: 0xE +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\asm.c +; OBJ64-NEXT: +0x0: 3 +; FIXME: An empty __asm stmt creates an extra entry. +; OBJ64-NEXT: +0x4: 4 +; OBJ64-NEXT: +0x4: 5 +; OBJ64-NEXT: +0x9: 6 +; OBJ64-NEXT: ] +; OBJ64-NEXT: ] +; OBJ64: } + +; Function Attrs: nounwind +define void @f() #0 { +entry: + call void asm sideeffect inteldialect ".align 4", "~{dirflag},~{fpsr},~{flags}"() #2, !dbg !12 + call void @g(), !dbg !13 + ret void, !dbg !14 +} + +declare void @g() #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10} +!llvm.ident = !{!11} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @f, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [f] +!5 = metadata !{metadata !"asm.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/asm.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!10 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!11 = metadata !{metadata !"clang version 3.5 "} +!12 = metadata !{i32 4, i32 0, metadata !4, null} +!13 = metadata !{i32 5, i32 0, metadata !4, null} +!14 = metadata !{i32 6, i32 0, metadata !4, null} Index: test/DebugInfo/X86/CodeView/multifile.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/multifile.ll @@ -0,0 +1,256 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X86 %s +; RUN: llc -mtriple=i686-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ32 %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X64 %s + +; This LL file was generated by running clang on the following code: +; D:\input.c: +; 1 void g(void); +; 2 +; 3 void f() { +; 4 #line 1 "one.c" +; 5 g(void); +; 6 #line 2 "two.c" +; 7 g(void); +; 8 #line 7 "one.c" +; 9 g(void); +; 10 } + +; X86: _f: +; X86-NEXT: # BB +; X86-NEXT: [[CALL_LINE_1:.*]]:{{$}} +; X86-NEXT: calll _g +; X86-NEXT: [[CALL_LINE_2:.*]]:{{$}} +; X86-NEXT: calll _g +; X86-NEXT: [[CALL_LINE_3:.*]]:{{$}} +; X86-NEXT: calll _g +; X86-NEXT: [[RETURN_STMT:.*]]: +; X86-NEXT: ret +; X86-NEXT: [[END_OF_F:.*]]: +; +; X86: .section .debug$S,"rn" +; X86-NEXT: .long 4 +; X86-NEXT: .long 242 +; X86-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X86-NEXT: [[F2_START]]: +; X86-NEXT: .secrel32 _f +; X86-NEXT: .secref _f +; X86-NEXT: .offset from _f to [[END_OF_F]] +; Segment for file 'D:\\one.c' begins +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 0 +; X86-NEXT: .long 1 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _f to [[CALL_LINE_1]] +; X86-NEXT: .long 1 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\two.c' begins +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 8 +; X86-NEXT: .long 1 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _f to [[CALL_LINE_2]] +; X86-NEXT: .long 2 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; A new segment for file 'D:\\one.c' begins +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 0 +; X86-NEXT: .long 2 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _f to [[CALL_LINE_3]] +; X86-NEXT: .long 7 +; X86-NEXT: .offset from _f to [[RETURN_STMT]] +; X86-NEXT: .long 8 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; X86-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X86-NEXT: .long 244 +; X86-NEXT: .long 16 +; X86-NEXT: .long 1 +; X86-NEXT: .long 0 +; X86-NEXT: .long 10 +; X86-NEXT: .long 0 +; String table +; X86-NEXT: .long 243 +; X86-NEXT: .long 19 +; X86-NEXT: .byte 0 +; X86-NEXT: .ascii "D:\\one.c" +; X86-NEXT: .byte 0 +; X86-NEXT: .ascii "D:\\two.c" +; X86-NEXT: .byte 0 +; X86-NEXT: .zero 1 + +; OBJ32: Section { +; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ32: Characteristics [ (0x42100040) +; OBJ32: ] +; OBJ32: Relocations [ +; OBJ32-NEXT: 0xC IMAGE_REL_I386_SECREL _f +; OBJ32-NEXT: 0x10 IMAGE_REL_I386_SECTION _f +; OBJ32-NEXT: ] +; OBJ32: FunctionLineTable [ +; OBJ32-NEXT: Name: _f +; OBJ32-NEXT: CodeSize: 0x10 +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\one.c +; OBJ32-NEXT: +0x0: 1 +; OBJ32-NEXT: ] +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\two.c +; OBJ32-NEXT: +0x5: 2 +; OBJ32-NEXT: ] +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\one.c +; OBJ32-NEXT: +0xA: 7 +; OBJ32-NEXT: +0xF: 8 +; OBJ32-NEXT: ] +; OBJ32-NEXT: ] +; OBJ32: } + +; X64: f: +; X64-NEXT: [[START:.*]]:{{$}} +; X64-NEXT: # BB +; X64-NEXT: subq $40, %rsp +; X64-NEXT: [[CALL_LINE_1:.*]]:{{$}} +; X64-NEXT: callq g +; X64-NEXT: [[CALL_LINE_2:.*]]:{{$}} +; X64-NEXT: callq g +; X64-NEXT: [[CALL_LINE_3:.*]]:{{$}} +; X64-NEXT: callq g +; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_F:.*]]: +; +; X64: .section .debug$S,"rn" +; X64-NEXT: .long 4 +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 f +; X64-NEXT: .secref f +; X64-NEXT: .offset from f to [[END_OF_F]] +; Segment for file 'D:\\input.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 1 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[START]] +; X64-NEXT: .long 3 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\one.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 8 +; X64-NEXT: .long 1 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[CALL_LINE_1]] +; X64-NEXT: .long 1 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\two.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 16 +; X64-NEXT: .long 1 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[CALL_LINE_2]] +; X64-NEXT: .long 2 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; A new segment for file 'D:\\one.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 8 +; X64-NEXT: .long 2 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[CALL_LINE_3]] +; X64-NEXT: .long 7 +; X64-NEXT: .offset from f to [[EPILOG_AND_RET]] +; X64-NEXT: .long 8 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X64-NEXT: .long 244 +; X64-NEXT: .long 24 +; X64-NEXT: .long 1 +; X64-NEXT: .long 0 +; X64-NEXT: .long 12 +; X64-NEXT: .long 0 +; X64-NEXT: .long 21 +; X64-NEXT: .long 0 +; String table +; X64-NEXT: .long 243 +; X64-NEXT: .long 30 +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\input.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\one.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\two.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .zero 2 + +; OBJ64: Section { +; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ64: Characteristics [ (0x42100040) +; OBJ64: ] +; OBJ64: Relocations [ +; OBJ64-NEXT: 0xC IMAGE_REL_AMD64_SECREL f +; OBJ64-NEXT: 0x10 IMAGE_REL_AMD64_SECTION f +; OBJ64-NEXT: ] +; OBJ64: FunctionLineTable [ +; OBJ64-NEXT: Name: f +; OBJ64-NEXT: CodeSize: 0x18 +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\input.c +; OBJ64-NEXT: +0x0: 3 +; OBJ64-NEXT: ] +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\one.c +; OBJ64-NEXT: +0x4: 1 +; OBJ64-NEXT: ] +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\two.c +; OBJ64-NEXT: +0x0: 2 +; OBJ64-NEXT: ] +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\one.c +; OBJ64-NEXT: +0xE: 7 +; OBJ64-NEXT: +0x13: 8 +; OBJ64-NEXT: ] +; OBJ64-NEXT: ] +; OBJ64: } + +; Function Attrs: nounwind +define void @f() #0 { +entry: + call void @g(), !dbg !12 + call void @g(), !dbg !15 + call void @g(), !dbg !18 + ret void, !dbg !19 +} + +declare void @g() #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10} +!llvm.ident = !{!11} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @f, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [f] +!5 = metadata !{metadata !"input.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/input.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!10 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!11 = metadata !{metadata !"clang version 3.5 "} +!12 = metadata !{i32 1, i32 0, metadata !13, null} +!13 = metadata !{i32 786443, metadata !14, metadata !4} ; [ DW_TAG_lexical_block ] [D:\/one.c] +!14 = metadata !{metadata !"one.c", metadata !"D:\5C"} +!15 = metadata !{i32 2, i32 0, metadata !16, null} +!16 = metadata !{i32 786443, metadata !17, metadata !4} ; [ DW_TAG_lexical_block ] [D:\/two.c] +!17 = metadata !{metadata !"two.c", metadata !"D:\5C"} +!18 = metadata !{i32 7, i32 0, metadata !13, null} +!19 = metadata !{i32 8, i32 0, metadata !13, null} ; [ DW_TAG_imported_declaration ] Index: test/DebugInfo/X86/CodeView/multifunction.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/multifunction.ll @@ -0,0 +1,378 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X86 %s +; RUN: llc -mtriple=i686-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ32 %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X64 %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ64 %s + +; This LL file was generated by running clang on the following code: +; D:\source.c: +; 1 void z(void); +; 2 +; 3 void x(void) { +; 4 z(); +; 5 } +; 6 +; 7 void y(void) { +; 8 z(); +; 9 } +; 10 +; 11 void f(void) { +; 12 x(); +; 13 y(); +; 14 z(); +; 15 } + + +; X86: _x: +; X86-NEXT: # BB +; X86-NEXT: [[X_CALL:.*]]:{{$}} +; X86-NEXT: calll _z +; X86-NEXT: [[X_RETURN:.*]]: +; X86-NEXT: ret +; X86-NEXT: [[END_OF_X:.*]]: +; +; X86: _y: +; X86-NEXT: # BB +; X86-NEXT: [[Y_CALL:.*]]:{{$}} +; X86-NEXT: calll _z +; X86-NEXT: [[Y_RETURN:.*]]: +; X86-NEXT: ret +; X86-NEXT: [[END_OF_Y:.*]]: +; +; X86: _f: +; X86-NEXT: # BB +; X86-NEXT: [[F_CALLS_X:.*]]:{{$}} +; X86-NEXT: calll _x +; X86-NEXT: [[F_CALLS_Y:.*]]: +; X86-NEXT: calll _y +; X86-NEXT: [[F_CALLS_Z:.*]]: +; X86-NEXT: calll _z +; X86-NEXT: [[F_RETURN:.*]]: +; X86-NEXT: ret +; X86-NEXT: [[END_OF_F:.*]]: +; +; X86: .section .debug$S,"rn" +; X86-NEXT: .long 4 +; Line table subsection for x +; X86-NEXT: .long 242 +; X86-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X86-NEXT: [[F2_START]]: +; X86-NEXT: .secrel32 _x +; X86-NEXT: .secref _x +; X86-NEXT: .offset from _x to [[END_OF_X]] +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 0 +; X86-NEXT: .long 2 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _x to [[X_CALL]] +; X86-NEXT: .long 4 +; X86-NEXT: .offset from _x to [[X_RETURN]] +; X86-NEXT: .long 5 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; X86-NEXT: [[F2_END]]: +; Line table subsection for y +; X86-NEXT: .long 242 +; X86-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X86-NEXT: [[F2_START]]: +; X86-NEXT: .secrel32 _y +; X86-NEXT: .secref _y +; X86-NEXT: .offset from _y to [[END_OF_Y]] +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 0 +; X86-NEXT: .long 2 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _y to [[Y_CALL]] +; X86-NEXT: .long 8 +; X86-NEXT: .offset from _y to [[Y_RETURN]] +; X86-NEXT: .long 9 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; X86-NEXT: [[F2_END]]: +; Line table subsection for f +; X86-NEXT: .long 242 +; X86-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X86-NEXT: [[F2_START]]: +; X86-NEXT: .secrel32 _f +; X86-NEXT: .secref _f +; X86-NEXT: .offset from _f to [[END_OF_F]] +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 0 +; X86-NEXT: .long 4 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _f to [[F_CALLS_X]] +; X86-NEXT: .long 12 +; X86-NEXT: .offset from _f to [[F_CALLS_Y]] +; X86-NEXT: .long 13 +; X86-NEXT: .offset from _f to [[F_CALLS_Z]] +; X86-NEXT: .long 14 +; X86-NEXT: .offset from _f to [[F_RETURN]] +; X86-NEXT: .long 15 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; X86-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X86-NEXT: .long 244 +; X86-NEXT: .long 8 +; X86-NEXT: .long 1 +; X86-NEXT: .long 0 +; String table +; X86-NEXT: .long 243 +; X86-NEXT: .long 13 +; X86-NEXT: .byte 0 +; X86-NEXT: .ascii "D:\\source.c" +; X86-NEXT: .byte 0 +; X86-NEXT: .zero 3 + +; OBJ32: Section { +; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ32: Characteristics [ (0x42100040) +; OBJ32: ] +; OBJ32: Relocations [ +; OBJ32-NEXT: 0xC IMAGE_REL_I386_SECREL _x +; OBJ32-NEXT: 0x10 IMAGE_REL_I386_SECTION _x +; OBJ32-NEXT: 0x3C IMAGE_REL_I386_SECREL _y +; OBJ32-NEXT: 0x40 IMAGE_REL_I386_SECTION _y +; OBJ32-NEXT: 0x6C IMAGE_REL_I386_SECREL _f +; OBJ32-NEXT: 0x70 IMAGE_REL_I386_SECTION _f +; OBJ32-NEXT: ] +; OBJ32: FunctionLineTable [ +; OBJ32-NEXT: Name: _x +; OBJ32-NEXT: CodeSize: 0x6 +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\source.c +; OBJ32-NEXT: +0x0: 4 +; OBJ32-NEXT: +0x5: 5 +; OBJ32-NEXT: ] +; OBJ32-NEXT: ] +; OBJ32-NEXT: FunctionLineTable [ +; OBJ32-NEXT: Name: _y +; OBJ32-NEXT: CodeSize: 0x6 +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\source.c +; OBJ32-NEXT: +0x0: 8 +; OBJ32-NEXT: +0x5: 9 +; OBJ32-NEXT: ] +; OBJ32-NEXT: ] +; OBJ32-NEXT: FunctionLineTable [ +; OBJ32-NEXT: Name: _f +; OBJ32-NEXT: CodeSize: 0x10 +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\source.c +; OBJ32-NEXT: +0x0: 12 +; OBJ32-NEXT: +0x5: 13 +; OBJ32-NEXT: +0xA: 14 +; OBJ32-NEXT: +0xF: 15 +; OBJ32-NEXT: ] +; OBJ32-NEXT: ] +; OBJ32: } + +; X64: x: +; X64-NEXT: [[X_START:.*]]:{{$}} +; X64-NEXT: # BB +; X64-NEXT: subq $40, %rsp +; X64-NEXT: [[X_CALL_LINE:.*]]:{{$}} +; X64-NEXT: callq z +; X64-NEXT: [[X_EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_X:.*]]: +; +; X64: y: +; X64-NEXT: [[Y_START:.*]]:{{$}} +; X64-NEXT: # BB +; X64-NEXT: subq $40, %rsp +; X64-NEXT: [[Y_CALL_LINE:.*]]:{{$}} +; X64-NEXT: callq z +; X64-NEXT: [[Y_EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_Y:.*]]: +; +; X64: f: +; X64-NEXT: [[F_START:.*]]:{{$}} +; X64-NEXT: # BB +; X64-NEXT: subq $40, %rsp +; X64-NEXT: [[F_CALLS_X:.*]]:{{$}} +; X64-NEXT: callq x +; X64-NEXT: [[F_CALLS_Y:.*]]: +; X64-NEXT: callq y +; X64-NEXT: [[F_CALLS_Z:.*]]: +; X64-NEXT: callq z +; X64-NEXT: [[F_EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_F:.*]]: +; +; X64: .section .debug$S,"rn" +; X64-NEXT: .long 4 +; Line table subsection for x +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 x +; X64-NEXT: .secref x +; X64-NEXT: .offset from x to [[END_OF_X]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 3 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from x to [[X_START]] +; X64-NEXT: .long 3 +; X64-NEXT: .offset from x to [[X_CALL_LINE]] +; X64-NEXT: .long 4 +; X64-NEXT: .offset from x to [[X_EPILOG_AND_RET]] +; X64-NEXT: .long 5 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; Line table subsection for y +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 y +; X64-NEXT: .secref y +; X64-NEXT: .offset from y to [[END_OF_Y]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 3 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from y to [[Y_START]] +; X64-NEXT: .long 7 +; X64-NEXT: .offset from y to [[Y_CALL_LINE]] +; X64-NEXT: .long 8 +; X64-NEXT: .offset from y to [[Y_EPILOG_AND_RET]] +; X64-NEXT: .long 9 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; Line table subsection for f +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 f +; X64-NEXT: .secref f +; X64-NEXT: .offset from f to [[END_OF_F]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 5 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[F_START]] +; X64-NEXT: .long 11 +; X64-NEXT: .offset from f to [[F_CALLS_X]] +; X64-NEXT: .long 12 +; X64-NEXT: .offset from f to [[F_CALLS_Y]] +; X64-NEXT: .long 13 +; X64-NEXT: .offset from f to [[F_CALLS_Z]] +; X64-NEXT: .long 14 +; X64-NEXT: .offset from f to [[F_EPILOG_AND_RET]] +; X64-NEXT: .long 15 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X64-NEXT: .long 244 +; X64-NEXT: .long 8 +; X64-NEXT: .long 1 +; X64-NEXT: .long 0 +; String table +; X64-NEXT: .long 243 +; X64-NEXT: .long 13 +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\source.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .zero 3 + +; OBJ64: Section { +; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ64: Characteristics [ (0x42100040) +; OBJ64: ] +; OBJ64: Relocations [ +; OBJ64-NEXT: 0xC IMAGE_REL_AMD64_SECREL x +; OBJ64-NEXT: 0x10 IMAGE_REL_AMD64_SECTION x +; OBJ64-NEXT: 0x44 IMAGE_REL_AMD64_SECREL y +; OBJ64-NEXT: 0x48 IMAGE_REL_AMD64_SECTION y +; OBJ64-NEXT: 0x7C IMAGE_REL_AMD64_SECREL f +; OBJ64-NEXT: 0x80 IMAGE_REL_AMD64_SECTION f +; OBJ64-NEXT: ] +; OBJ64: FunctionLineTable [ +; OBJ64-NEXT: Name: x +; OBJ64-NEXT: CodeSize: 0xE +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\source.c +; OBJ64-NEXT: +0x0: 3 +; OBJ64-NEXT: +0x4: 4 +; OBJ64-NEXT: +0x9: 5 +; OBJ64-NEXT: ] +; OBJ64-NEXT: ] +; OBJ64-NEXT: FunctionLineTable [ +; OBJ64-NEXT: Name: y +; OBJ64-NEXT: CodeSize: 0xE +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\source.c +; OBJ64-NEXT: +0x0: 7 +; OBJ64-NEXT: +0x4: 8 +; OBJ64-NEXT: +0x9: 9 +; OBJ64-NEXT: ] +; OBJ64-NEXT: ] +; OBJ64-NEXT: FunctionLineTable [ +; OBJ64-NEXT: Name: f +; OBJ64-NEXT: CodeSize: 0x18 +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\source.c +; OBJ64-NEXT: +0x0: 11 +; OBJ64-NEXT: +0x4: 12 +; OBJ64-NEXT: +0x9: 13 +; OBJ64-NEXT: +0xE: 14 +; OBJ64-NEXT: +0x13: 15 +; OBJ64-NEXT: ] +; OBJ64-NEXT: ] +; OBJ64: } + +; Function Attrs: nounwind +define void @x() #0 { +entry: + call void @z(), !dbg !14 + ret void, !dbg !15 +} + +declare void @z() #1 + +; Function Attrs: nounwind +define void @y() #0 { +entry: + call void @z(), !dbg !16 + ret void, !dbg !17 +} + +; Function Attrs: nounwind +define void @f() #0 { +entry: + call void @x(), !dbg !18 + call void @y(), !dbg !19 + call void @z(), !dbg !20 + ret void, !dbg !21 +} + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!11, !12} +!llvm.ident = !{!13} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4, metadata !9, metadata !10} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"x", metadata !"x", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @x, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [x] +!5 = metadata !{metadata !"source.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/source.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"y", metadata !"y", metadata !"", i32 7, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @y, null, null, metadata !2, i32 7} ; [ DW_TAG_subprogram ] [line 7] [def] [y] +!10 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 11, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @f, null, null, metadata !2, i32 11} ; [ DW_TAG_subprogram ] [line 11] [def] [f] +!11 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!12 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!13 = metadata !{metadata !"clang version 3.5 "} +!14 = metadata !{i32 4, i32 0, metadata !4, null} +!15 = metadata !{i32 5, i32 0, metadata !4, null} +!16 = metadata !{i32 8, i32 0, metadata !9, null} ; [ DW_TAG_imported_declaration ] +!17 = metadata !{i32 9, i32 0, metadata !9, null} +!18 = metadata !{i32 12, i32 0, metadata !10, null} +!19 = metadata !{i32 13, i32 0, metadata !10, null} +!20 = metadata !{i32 14, i32 0, metadata !10, null} +!21 = metadata !{i32 15, i32 0, metadata !10, null} Index: test/DebugInfo/X86/CodeView/simple.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/simple.ll @@ -0,0 +1,167 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X86 %s +; RUN: llc -mtriple=i686-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ32 %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X64 %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=obj -o - -O0 < %s | llvm-readobj -s -sr -codeview-linetables | FileCheck --check-prefix=OBJ64 %s + +; This LL file was generated by running clang on the following code: +; D:\test.c: +; 1 void g(void); +; 2 +; 3 void f(void) { +; 4 g(); +; 5 } + +; X86: _f: +; X86-NEXT: # BB +; X86-NEXT: [[CALL_LINE:^L.*]]:{{$}} +; X86-NEXT: calll _g +; X86-NEXT: [[RETURN_STMT:.*]]: +; X86-NEXT: ret +; X86-NEXT: [[END_OF_F:.*]]: +; +; X86: .section .debug$S,"rn" +; X86-NEXT: .long 4 +; X86-NEXT: .long 242 +; X86-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X86-NEXT: [[F2_START]]: +; X86-NEXT: .secrel32 _f +; X86-NEXT: .secref _f +; X86-NEXT: .offset from _f to [[END_OF_F]] +; X86-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X86-NEXT: .long 0 +; X86-NEXT: .long 2 +; X86-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X86-NEXT: .offset from _f to [[CALL_LINE]] +; X86-NEXT: .long 4 +; X86-NEXT: .offset from _f to [[RETURN_STMT]] +; X86-NEXT: .long 5 +; X86-NEXT: [[FILE_SEGMENT_END]]: +; X86-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X86-NEXT: .long 244 +; X86-NEXT: .long 8 +; X86-NEXT: .long 1 +; X86-NEXT: .long 0 +; String table +; X86-NEXT: .long 243 +; X86-NEXT: .long 11 +; X86-NEXT: .byte 0 +; X86-NEXT: .ascii "D:\\test.c" +; X86-NEXT: .byte 0 +; Padding +; X86-NEXT: .zero 1 + +; OBJ32: Section { +; OBJ32: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ32: Characteristics [ (0x42100040) +; OBJ32: ] +; OBJ32: Relocations [ +; OBJ32-NEXT: 0xC IMAGE_REL_I386_SECREL _f +; OBJ32-NEXT: 0x10 IMAGE_REL_I386_SECTION _f +; OBJ32-NEXT: ] +; OBJ32: FunctionLineTable [ +; OBJ32-NEXT: Name: _f +; OBJ32-NEXT: CodeSize: 0x6 +; OBJ32-NEXT: FilenameSegment [ +; OBJ32-NEXT: Filename: D:\test.c +; OBJ32-NEXT: +0x0: 4 +; OBJ32-NEXT: +0x5: 5 +; OBJ32-NEXT: ] +; OBJ32-NEXT: ] +; OBJ32: } + +; X64: f: +; X64-NEXT: [[START:.*]]:{{$}} +; X64-NEXT: # BB +; X64-NEXT: subq $40, %rsp +; X64-NEXT: [[CALL_LINE:.*]]:{{$}} +; X64-NEXT: callq g +; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_F:.*]]: +; +; X64: .section .debug$S,"rn" +; X64-NEXT: .long 4 +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 f +; X64-NEXT: .secref f +; X64-NEXT: .offset from f to [[END_OF_F]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 3 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[START]] +; X64-NEXT: .long 3 +; X64-NEXT: .offset from f to [[CALL_LINE]] +; X64-NEXT: .long 4 +; X64-NEXT: .offset from f to [[EPILOG_AND_RET]] +; X64-NEXT: .long 5 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X64-NEXT: .long 244 +; X64-NEXT: .long 8 +; X64-NEXT: .long 1 +; X64-NEXT: .long 0 +; String table +; X64-NEXT: .long 243 +; X64-NEXT: .long 11 +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\test.c" +; X64-NEXT: .byte 0 +; Padding +; X64-NEXT: .zero 1 + +; OBJ64: Section { +; OBJ64: Name: .debug$S (2E 64 65 62 75 67 24 53) +; OBJ64: Characteristics [ (0x42100040) +; OBJ64: ] +; OBJ64: Relocations [ +; OBJ64-NEXT: 0xC IMAGE_REL_AMD64_SECREL f +; OBJ64-NEXT: 0x10 IMAGE_REL_AMD64_SECTION f +; OBJ64-NEXT: ] +; OBJ64: FunctionLineTable [ +; OBJ64-NEXT: Name: f +; OBJ64-NEXT: CodeSize: 0xE +; OBJ64-NEXT: FilenameSegment [ +; OBJ64-NEXT: Filename: D:\test.c +; OBJ64-NEXT: +0x0: 3 +; OBJ64-NEXT: +0x4: 4 +; OBJ64-NEXT: +0x9: 5 +; OBJ64-NEXT: ] +; OBJ64-NEXT: ] +; OBJ64: } + +; Function Attrs: nounwind +define void @f() #0 { +entry: + call void @g(), !dbg !12 + ret void, !dbg !13 +} + +declare void @g() #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10} +!llvm.ident = !{!11} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void ()* @f, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [f] +!5 = metadata !{metadata !"test.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/test.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!10 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!11 = metadata !{metadata !"clang version 3.5 "} +!12 = metadata !{i32 4, i32 0, metadata !4, null} +!13 = metadata !{i32 5, i32 0, metadata !4, null} Index: tools/llvm-readobj/COFFDumper.cpp =================================================================== --- tools/llvm-readobj/COFFDumper.cpp +++ tools/llvm-readobj/COFFDumper.cpp @@ -24,6 +24,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Win64EH.h" @@ -76,6 +77,8 @@ void printUnwindCode(const Win64EH::UnwindInfo& UI, ArrayRef UCs); + void printCodeViewLineTables(section_iterator SecI); + void cacheRelocations(); error_code getSectionContents( @@ -648,6 +651,130 @@ } } +void COFFDumper::printCodeViewLineTables(section_iterator SecI) { + StringRef Data; + if (error(SecI->getContents(Data))) return; + + SmallVector FunctionNames; + StringMap FunctionLineTables; + StringRef FileIndexToStringOffsetTable; + StringRef StringTable; + + ListScope D(W, "CodeViewLineTables"); + { + DataExtractor DE(Data, true, 4); + uint32_t Offset = 0; + + W.printHex("Magic", DE.getU32(&Offset)); + + bool Finished = false; + while (DE.isValidOffset(Offset) && !Finished) { + uint32_t SubSectionType = DE.getU32(&Offset), + PayloadSize = DE.getU32(&Offset); + ListScope S(W, "Subsection"); + W.printHex("Type", SubSectionType); + W.printHex("PayloadSize", PayloadSize); + if (PayloadSize > Data.size() - Offset) { + error(object_error::parse_failed); + return; + } + StringRef Contents = Data.substr(Offset, PayloadSize); + W.printBinaryBlock("Contents", Contents); + + switch (SubSectionType) { + case 0xF2: { + // There should be at least three words to store two function + // relocations and size of the code. + if (PayloadSize < 12) { + error(object_error::parse_failed); + return; + } + + StringRef FunctionName; + if (error(resolveSymbolName(RelocMap[Obj->getCOFFSection(SecI)], Offset, + FunctionName))) + return; + W.printString("FunctionName", FunctionName); + if (FunctionLineTables.count(FunctionName) != 0) { + // Saw debug info for this function already? + error(object_error::parse_failed); + return; + } + + FunctionLineTables[FunctionName] = Contents; + FunctionNames.push_back(FunctionName); + break; + } + case 0xF4: + if (PayloadSize == 0 || FileIndexToStringOffsetTable.data() != 0) { + // Empty or duplicate 0xF4 subsection. + error(object_error::parse_failed); + return; + } + FileIndexToStringOffsetTable = Contents; + break; + case 0xF3: + if (PayloadSize == 0 || StringTable.data() != 0 || + Contents.back() != '\0') { + // Empty or duplicate or non-null-terminated 0xF3 subsection. + error(object_error::parse_failed); + return; + } + StringTable = Contents; + break; + } + Offset += PayloadSize; + + // Align the reading pointer by 4. + Offset += (-Offset) % 4; + } + } + + for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) { + StringRef Name = FunctionNames[I]; + ListScope S(W, "FunctionLineTable"); + W.printString("Name", Name); + + DataExtractor DE(FunctionLineTables[Name], true, 4); + uint32_t Offset = 8; // Skip relocations + uint32_t FunctionSize = DE.getU32(&Offset); + W.printHex("CodeSize", FunctionSize); + while (DE.isValidOffset(Offset)) { + uint32_t OffsetInF4 = DE.getU32(&Offset), + SegmentLength = DE.getU32(&Offset), + FullSegmentSize = DE.getU32(&Offset), + OffsetInF3; + { + DataExtractor SDE(FileIndexToStringOffsetTable, true, 4); + uint32_t OffsetInSDE = OffsetInF4; + if (!SDE.isValidOffset(OffsetInSDE)) { + error(object_error::parse_failed); + return; + } + OffsetInF3 = SDE.getU32(&OffsetInSDE); + } + + if (OffsetInF3 == 0 || StringTable.data()[OffsetInF3 - 1] != '\0') { + // Each string in an F3 subsection should be preceded by a null + // character. + error(object_error::parse_failed); + return; + } + + StringRef Filename(StringTable.data() + OffsetInF3); + ListScope S(W, "FilenameSegment"); + W.printString("Filename", Filename); + for (unsigned J = 0; J != SegmentLength; ++J) { + uint32_t PC = DE.getU32(&Offset), + LineNumber = DE.getU32(&Offset) & 0x7fffffff; + char Buffer[32]; + format("+0x%X", PC).snprint(Buffer, 32); + W.printNumber(Buffer, LineNumber); + } + } + } +} + void COFFDumper::printSections() { error_code EC; @@ -707,6 +834,9 @@ } } + if (Name == ".debug$S" && opts::CodeViewLineTables) + printCodeViewLineTables(SecI); + if (opts::SectionData) { StringRef Data; if (error(SecI->getContents(Data))) break; Index: tools/llvm-readobj/llvm-readobj.h =================================================================== --- tools/llvm-readobj/llvm-readobj.h +++ tools/llvm-readobj/llvm-readobj.h @@ -38,6 +38,7 @@ extern llvm::cl::opt DynamicSymbols; extern llvm::cl::opt UnwindInfo; extern llvm::cl::opt ExpandRelocs; + extern llvm::cl::opt CodeViewLineTables; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \ Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -128,6 +128,10 @@ // -expand-relocs cl::opt ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); + + // -codeview-line + cl::opt CodeViewLineTables("codeview-linetables", + cl::desc("Display CodeView line table information")); } // namespace opts static int ReturnValue = EXIT_SUCCESS;