Index: docs/AMDGPUUsage.rst =================================================================== --- docs/AMDGPUUsage.rst +++ docs/AMDGPUUsage.rst @@ -798,7 +798,7 @@ DWARF ----- -Standard DWARF [DWARF]_ Version 2 sections can be generated. These contain +Standard DWARF [DWARF]_ Version 5 sections can be generated. These contain information that maps the code object executable code and data to the source language constructs. It can be used by tools such as debuggers and profilers. @@ -854,10 +854,65 @@ Source Text ~~~~~~~~~~~ -*This section is WIP.* +Source text for online-compiled programs (e.g. those compiled by the OpenCL +runtime) may be embedded into the DWARFv5 line table using the ``clang +-gembed-source`` option, described in table :ref:`amdgpu-debug-options`. -.. TODO - DWARF extension to include runtime generated source text. +For example: + +``-gembed-source`` + Enable embedded source DWARFv5 extension. +``-gno-embed-source`` + Disable the embedded source DWARFv5 extension. + + .. table:: AMDGPU Debug Options + :name: amdgpu-debug-options + + ==================== ================================================== + Debug Flag Description + ==================== ================================================== + -g[no-]embed-source Enable/disable embedding source text in DWARF + debug sections. Useful for environments where + source cannot be written to disk, such as + when performing online compilation. + ==================== ================================================== + +This option enables two extended content types in the DWARFv5 Line Number +Program Header, which are used to encode the presence of embedded source, as +well as the source itself. + + .. table:: AMDGPU DWARF Line Number Program Header Extended Content Types + :name: amdgpu-dwarf-extended-content-types + + ========================== ==================== + Content Type Form + ========================== ==================== + ``DW_LNCT_has_source`` ``DW_FORM_flag`` + ``DW_LNCT_source`` ``DW_FORM_string`` + ========================== ==================== + +When the has_source flag is true the source field will contain the UTF-8 +encoded, null-terminated source text with ``'\n'`` line endings. Consumers can +use the embedded source instead of attempting to discover the source on disk. +When the has_source flag is false, the source field will be an empty +null-terminated string. Consumers can access the file to get the source text. +In this way, a mix of files both with and without embedded source available can +be in the same program. + +The above content types appear in the ``file_name_entry_format`` field of the +line table prologue, and their corresponding values appear in the +``file_names`` field. The current encoding of the two content types is +documented in table :ref:`amdgpu-dwarf-extended-content-types-encoding` + + .. table:: AMDGPU DWARF Line Number Program Header Extended Content Types Encoding + :name: amdgpu-dwarf-extended-content-types-encoding + + ========================== ==================== + Content Type Value + ========================== ==================== + ``DW_LNCT_has_source`` 0x2001 + ``DW_LNCT_source`` 0x2002 + ========================== ==================== .. _amdgpu-code-conventions: Index: include/llvm-c/DebugInfo.h =================================================================== --- include/llvm-c/DebugInfo.h +++ include/llvm-c/DebugInfo.h @@ -206,11 +206,14 @@ * \param FilenameLen The length of the C string passed to \c Filename. * \param Directory Directory. * \param DirectoryLen The length of the C string passed to \c Directory. + * \param Source Source file text. + * \param SourceLen The length of the C string passed to \c Source. */ LLVMMetadataRef LLVMDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename, size_t FilenameLen, const char *Directory, - size_t DirectoryLen); + size_t DirectoryLen, LLVMBool SourcePresent, + const char *Source, size_t SourceLen); /** * Creates a new DebugLocation that describes a source location. Index: include/llvm/BinaryFormat/Dwarf.def =================================================================== --- include/llvm/BinaryFormat/Dwarf.def +++ include/llvm/BinaryFormat/Dwarf.def @@ -747,6 +747,8 @@ HANDLE_DW_LNCT(0x03, timestamp) HANDLE_DW_LNCT(0x04, size) HANDLE_DW_LNCT(0x05, MD5) +HANDLE_DW_LNCT(0x2001, has_source) +HANDLE_DW_LNCT(0x2002, source) // DWARF v5 Macro information. HANDLE_DW_MACRO(0x01, define) Index: include/llvm/DebugInfo/DIContext.h =================================================================== --- include/llvm/DebugInfo/DIContext.h +++ include/llvm/DebugInfo/DIContext.h @@ -31,6 +31,7 @@ struct DILineInfo { std::string FileName; std::string FunctionName; + Optional Source; uint32_t Line = 0; uint32_t Column = 0; uint32_t StartLine = 0; Index: include/llvm/DebugInfo/DWARF/DWARFDebugLine.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -10,6 +10,7 @@ #ifndef LLVM_DEBUGINFO_DWARFDEBUGLINE_H #define LLVM_DEBUGINFO_DWARFDEBUGLINE_H +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" @@ -36,6 +37,8 @@ uint64_t ModTime = 0; uint64_t Length = 0; MD5::MD5Result Checksum; + bool HasSource = false; + StringRef Source; }; struct Prologue { @@ -70,6 +73,7 @@ uint8_t OpcodeBase; /// For v5, whether filename entries provide an MD5 checksum. bool HasMD5; + bool HasSource; std::vector StandardOpcodeLengths; std::vector IncludeDirectories; std::vector FileNames; @@ -239,6 +243,8 @@ private: uint32_t findRowInSeq(const DWARFDebugLine::Sequence &Seq, uint64_t Address) const; + Optional getSourceByIndex(uint64_t FileIndex, + DILineInfoSpecifier::FileLineInfoKind Kind) const; }; const LineTable *getLineTable(uint32_t Offset) const; Index: include/llvm/IR/DIBuilder.h =================================================================== --- include/llvm/IR/DIBuilder.h +++ include/llvm/IR/DIBuilder.h @@ -19,6 +19,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -143,9 +144,11 @@ /// \param Directory Directory. /// \param CSKind Checksum kind (e.g. CSK_None, CSK_MD5, CSK_SHA1, etc.). /// \param Checksum Checksum data. + /// \param Source Optional source text. DIFile *createFile(StringRef Filename, StringRef Directory, DIFile::ChecksumKind CSKind = DIFile::CSK_None, - StringRef Checksum = StringRef()); + StringRef Checksum = StringRef(), + Optional Source = None); /// Create debugging information entry for a macro. /// \param Parent Macro parent (could be nullptr). Index: include/llvm/IR/DebugInfoMetadata.h =================================================================== --- include/llvm/IR/DebugInfoMetadata.h +++ include/llvm/IR/DebugInfoMetadata.h @@ -453,6 +453,7 @@ inline StringRef getFilename() const; inline StringRef getDirectory() const; + inline Optional getSource() const; StringRef getName() const; DIScopeRef getScope() const; @@ -508,38 +509,46 @@ private: ChecksumKind CSKind; + Optional Source; DIFile(LLVMContext &C, StorageType Storage, ChecksumKind CSK, - ArrayRef Ops) + Optional Src, ArrayRef Ops) : DIScope(C, DIFileKind, Storage, dwarf::DW_TAG_file_type, Ops), - CSKind(CSK) {} + CSKind(CSK), + Source(Src) {} ~DIFile() = default; static DIFile *getImpl(LLVMContext &Context, StringRef Filename, StringRef Directory, ChecksumKind CSK, StringRef CS, - StorageType Storage, bool ShouldCreate = true) { + Optional Source, StorageType Storage, + bool ShouldCreate = true) { return getImpl(Context, getCanonicalMDString(Context, Filename), - getCanonicalMDString(Context, Directory), CSK, - getCanonicalMDString(Context, CS), Storage, ShouldCreate); + getCanonicalMDString(Context, Directory), + CSK, getCanonicalMDString(Context, CS), + Source ? Optional(getCanonicalMDString(Context, *Source)) : None, + Storage, ShouldCreate); } static DIFile *getImpl(LLVMContext &Context, MDString *Filename, MDString *Directory, ChecksumKind CSK, MDString *CS, - StorageType Storage, bool ShouldCreate = true); + Optional Source, StorageType Storage, + bool ShouldCreate = true); TempDIFile cloneImpl() const { return getTemporary(getContext(), getFilename(), getDirectory(), - getChecksumKind(), getChecksum()); + getChecksumKind(), getChecksum(), getSource()); } public: DEFINE_MDNODE_GET(DIFile, (StringRef Filename, StringRef Directory, ChecksumKind CSK = CSK_None, - StringRef CS = StringRef()), - (Filename, Directory, CSK, CS)) + StringRef CS = StringRef(), + Optional Source = None), + (Filename, Directory, CSK, CS, Source)) DEFINE_MDNODE_GET(DIFile, (MDString * Filename, MDString *Directory, ChecksumKind CSK = CSK_None, - MDString *CS = nullptr), - (Filename, Directory, CSK, CS)) + MDString *CS = nullptr, + Optional Source = None), + (Filename, Directory, CSK, CS, Source)) TempDIFile clone() const { return cloneImpl(); } @@ -548,10 +557,14 @@ StringRef getChecksum() const { return getStringOperand(2); } ChecksumKind getChecksumKind() const { return CSKind; } StringRef getChecksumKindAsString() const; + Optional getSource() const { + return Source ? Optional((*Source)->getString()) : None; + } MDString *getRawFilename() const { return getOperandAs(0); } MDString *getRawDirectory() const { return getOperandAs(1); } MDString *getRawChecksum() const { return getOperandAs(2); } + Optional getRawSource() const { return Source; } static ChecksumKind getChecksumKind(StringRef CSKindStr); @@ -572,6 +585,12 @@ return ""; } +Optional DIScope::getSource() const { + if (auto *F = getFile()) + return F->getSource(); + return None; +} + /// Base class for types. /// /// TODO: Remove the hardcoded name and context, since many types don't use @@ -1365,6 +1384,7 @@ DIFile *getFile() const { return getScope()->getFile(); } StringRef getFilename() const { return getScope()->getFilename(); } StringRef getDirectory() const { return getScope()->getDirectory(); } + Optional getSource() const { return getScope()->getSource(); } /// Get the scope where this is inlined. /// @@ -2134,6 +2154,12 @@ return ""; } + Optional getSource() const { + if (auto *F = getFile()) + return F->getSource(); + return None; + } + Metadata *getRawScope() const { return getOperand(0); } MDString *getRawName() const { return getOperandAs(1); } Metadata *getRawFile() const { return getOperand(2); } @@ -2575,6 +2601,12 @@ return ""; } + Optional getSource() const { + if (auto *F = getFile()) + return F->getSource(); + return None; + } + MDString *getRawName() const { return getOperandAs(0); } Metadata *getRawFile() const { return getOperand(1); } MDString *getRawGetterName() const { return getOperandAs(2); } Index: include/llvm/MC/MCContext.h =================================================================== --- include/llvm/MC/MCContext.h +++ include/llvm/MC/MCContext.h @@ -11,6 +11,7 @@ #define LLVM_MC_MCCONTEXT_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -491,7 +492,7 @@ /// Creates an entry in the dwarf file and directory tables. unsigned getDwarfFile(StringRef Directory, StringRef FileName, unsigned FileNumber, MD5::MD5Result *Checksum, - unsigned CUID); + Optional Source, unsigned CUID); bool isValidDwarfFileNumber(unsigned FileNumber, unsigned CUID = 0); Index: include/llvm/MC/MCDwarf.h =================================================================== --- include/llvm/MC/MCDwarf.h +++ include/llvm/MC/MCDwarf.h @@ -16,6 +16,7 @@ #define LLVM_MC_MCDWARF_H #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -46,7 +47,6 @@ /// index 0 is not used and not a valid dwarf file number). struct MCDwarfFile { // \brief The base name of the file without its directory path. - // The StringRef references memory allocated in the MCContext. std::string Name; // \brief The index into the list of directory names for this file name. @@ -55,6 +55,10 @@ /// The MD5 checksum, if there is one. Non-owning pointer to data allocated /// in MCContext. MD5::MD5Result *Checksum = nullptr; + + // \brief The source code of the file. Non-owning reference to data allocated + // in MCContext. + Optional Source; }; /// \brief Instances of this class represent the information from a @@ -209,11 +213,13 @@ StringMap SourceIdMap; StringRef CompilationDir; bool HasMD5 = false; + bool HasSource = false; MCDwarfLineTableHeader() = default; unsigned getFile(StringRef &Directory, StringRef &FileName, - MD5::MD5Result *Checksum, unsigned FileNumber = 0); + MD5::MD5Result *Checksum, Optional &Source, + unsigned FileNumber = 0); std::pair Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params) const; std::pair @@ -234,8 +240,8 @@ } unsigned getFile(StringRef Directory, StringRef FileName, - MD5::MD5Result *Checksum) { - return Header.getFile(Directory, FileName, Checksum); + MD5::MD5Result *Checksum, Optional Source) { + return Header.getFile(Directory, FileName, Checksum, Source); } void Emit(MCStreamer &MCOS, MCDwarfLineTableParams Params) const; @@ -253,7 +259,8 @@ void EmitCU(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params) const; unsigned getFile(StringRef &Directory, StringRef &FileName, - MD5::MD5Result *Checksum, unsigned FileNumber = 0); + MD5::MD5Result *Checksum, Optional &Source, + unsigned FileNumber = 0); MCSymbol *getLabel() const { return Header.Label; Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -16,6 +16,8 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCDirectives.h" @@ -754,11 +756,15 @@ /// '.ident "version foo"' assembler directive. virtual void EmitIdent(StringRef IdentString) {} - /// \brief Associate a filename with a specified logical file number. This - /// implements the DWARF2 '.file 4 "foo.c"' assembler directive. + /// \brief Associate a filename with a specified logical file number. + /// Also associate a directory, optional checksum, and optional source + /// text with the logical file. This implements the DWARF2 + /// '.file 4 "dir/foo.c"' assembler directive, and the DWARF5 + /// '.file 4 "dir/foo.c" md5 "..." source "..."' assembler directive. virtual unsigned EmitDwarfFileDirective(unsigned FileNo, StringRef Directory, StringRef Filename, MD5::MD5Result *Checksum = nullptr, + Optional Source = None, unsigned CUID = 0); /// \brief This implements the DWARF2 '.loc fileno lineno ...' assembler Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -4195,20 +4195,27 @@ } /// ParseDIFileType: -/// ::= !DIFileType(filename: "path/to/file", directory: "/path/to/dir" +/// ::= !DIFileType(filename: "path/to/file", directory: "/path/to/dir", /// checksumkind: CSK_MD5, -/// checksum: "000102030405060708090a0b0c0d0e0f") +/// checksum: "000102030405060708090a0b0c0d0e0f", +/// source: "source file contents") bool LLParser::ParseDIFile(MDNode *&Result, bool IsDistinct) { #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ REQUIRED(filename, MDStringField, ); \ REQUIRED(directory, MDStringField, ); \ OPTIONAL(checksumkind, ChecksumKindField, ); \ - OPTIONAL(checksum, MDStringField, ); + OPTIONAL(checksum, MDStringField, ); \ + OPTIONAL(source, MDStringField, ); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS + Optional OptSource; + if (source.Seen) + OptSource = source.Val; + Result = GET_OR_DISTINCT(DIFile, (Context, filename.Val, directory.Val, - checksumkind.Val, checksum.Val)); + checksumkind.Val, checksum.Val, + OptSource)); return false; } Index: lib/Bitcode/Reader/MetadataLoader.cpp =================================================================== --- lib/Bitcode/Reader/MetadataLoader.cpp +++ lib/Bitcode/Reader/MetadataLoader.cpp @@ -1346,7 +1346,7 @@ } case bitc::METADATA_FILE: { - if (Record.size() != 3 && Record.size() != 5) + if (Record.size() < 3 || Record.size() > 6) return error("Invalid record"); IsDistinct = Record[0]; @@ -1354,9 +1354,10 @@ GET_OR_DISTINCT( DIFile, (Context, getMDString(Record[1]), getMDString(Record[2]), - Record.size() == 3 ? DIFile::CSK_None - : static_cast(Record[3]), - Record.size() == 3 ? nullptr : getMDString(Record[4]))), + Record.size() > 3 ? static_cast(Record[3]) + : DIFile::CSK_None, + Record.size() > 4 ? getMDString(Record[4]) : nullptr, + Record.size() > 5 ? Optional(getMDString(Record[5])) : None)), NextMetadataNo); NextMetadataNo++; break; Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1552,6 +1552,9 @@ Record.push_back(VE.getMetadataOrNullID(N->getRawDirectory())); Record.push_back(N->getChecksumKind()); Record.push_back(VE.getMetadataOrNullID(N->getRawChecksum())); + auto Source = N->getRawSource(); + if (Source) + Record.push_back(VE.getMetadataOrNullID(*Source)); Stream.EmitRecord(bitc::METADATA_FILE, Record, Abbrev); Record.clear(); Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -102,9 +102,10 @@ // extend .file to support this. unsigned CUID = Asm->OutStreamer->hasRawTextSupport() ? 0 : getUniqueID(); if (!File) - return Asm->OutStreamer->EmitDwarfFileDirective(0, "", "", nullptr, CUID); + return Asm->OutStreamer->EmitDwarfFileDirective(0, "", "", nullptr, None, CUID); return Asm->OutStreamer->EmitDwarfFileDirective( - 0, File->getDirectory(), File->getFilename(), getMD5AsBytes(File), CUID); + 0, File->getDirectory(), File->getFilename(), getMD5AsBytes(File), + File->getSource(), CUID); } DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE( Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1374,7 +1374,7 @@ void DwarfDebug::recordSourceLine(unsigned Line, unsigned Col, const MDNode *S, unsigned Flags) { StringRef Fn; - unsigned Src = 1; + unsigned FileNo = 1; unsigned Discriminator = 0; if (auto *Scope = cast_or_null(S)) { Fn = Scope->getFilename(); @@ -1383,10 +1383,10 @@ Discriminator = LBF->getDiscriminator(); unsigned CUID = Asm->OutStreamer->getContext().getDwarfCompileUnitID(); - Src = static_cast(*InfoHolder.getUnits()[CUID]) + FileNo = static_cast(*InfoHolder.getUnits()[CUID]) .getOrCreateSourceID(Scope->getFile()); } - Asm->OutStreamer->EmitDwarfLocDirective(Src, Line, Col, Flags, 0, + Asm->OutStreamer->EmitDwarfLocDirective(FileNo, Line, Col, Flags, 0, Discriminator, Fn); } Index: lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -295,7 +295,9 @@ unsigned DwarfTypeUnit::getOrCreateSourceID(const DIFile *File) { return SplitLineTable ? SplitLineTable->getFile(File->getDirectory(), - File->getFilename(), getMD5AsBytes(File)) + File->getFilename(), + getMD5AsBytes(File), + File->getSource()) : getCU().getOrCreateSourceID(File); } Index: lib/DebugInfo/DWARF/DWARFDebugLine.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -8,6 +8,8 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -22,6 +24,7 @@ #include #include #include +#include #include using namespace llvm; @@ -49,6 +52,7 @@ OpcodeBase = 0; FormParams = DWARFFormParams({0, 0, DWARF32}); HasMD5 = false; + HasSource = false; StandardOpcodeLengths.clear(); IncludeDirectories.clear(); FileNames.clear(); @@ -99,6 +103,11 @@ OS << format("0x%8.8" PRIx64 " 0x%8.8" PRIx64, FileEntry.ModTime, FileEntry.Length); OS << ' ' << FileEntry.Name << '\n'; + if (FileEntry.HasSource) { + std::istringstream source(FileEntry.Source.str()); + for (std::string line; std::getline(source, line); ) + OS << " " << line << '\n'; + } } } } @@ -134,7 +143,7 @@ // ran off the end of the prologue. static ContentDescriptors parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, - uint64_t EndPrologueOffset, bool *HasMD5) { + uint64_t EndPrologueOffset, bool *HasMD5, bool *HasSource) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; @@ -149,6 +158,8 @@ HasPath = true; else if (Descriptor.Type == dwarf::DW_LNCT_MD5 && HasMD5) *HasMD5 = true; + else if (Descriptor.Type == dwarf::DW_LNCT_has_source && HasSource) + *HasSource = true; Descriptors.push_back(Descriptor); } return HasPath ? Descriptors : ContentDescriptors(); @@ -158,12 +169,12 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, uint64_t EndPrologueOffset, const DWARFFormParams &FormParams, const DWARFContext &Ctx, - const DWARFUnit *U, bool &HasMD5, + const DWARFUnit *U, bool &HasMD5, bool &HasSource, std::vector &IncludeDirectories, std::vector &FileNames) { // Get the directory entry description. - ContentDescriptors DirDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); + ContentDescriptors DirDescriptors = parseV5EntryFormat( + DebugLineData, OffsetPtr, EndPrologueOffset, nullptr, nullptr); if (DirDescriptors.empty()) return false; @@ -189,7 +200,8 @@ // Get the file entry description. ContentDescriptors FileDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, &HasMD5); + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, &HasMD5, + &HasSource); if (FileDescriptors.empty()) return false; @@ -207,6 +219,12 @@ case DW_LNCT_path: FileEntry.Name = Value.getAsCString().getValue(); break; + case DW_LNCT_has_source: + FileEntry.HasSource = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_source: + FileEntry.Source = Value.getAsCString().getValue(); + break; case DW_LNCT_directory_index: FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue(); break; @@ -274,8 +292,8 @@ if (getVersion() >= 5) { if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - FormParams, Ctx, U, HasMD5, IncludeDirectories, - FileNames)) { + FormParams, Ctx, U, HasMD5, HasSource, + IncludeDirectories, FileNames)) { fprintf(stderr, "warning: parsing line table prologue at 0x%8.8" PRIx64 " found an invalid directory or file table description at" @@ -895,6 +913,17 @@ return FileIndex != 0 && FileIndex <= Prologue.FileNames.size(); } +Optional DWARFDebugLine::LineTable::getSourceByIndex(uint64_t FileIndex, + FileLineInfoKind Kind) const { + if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) + return None; + const FileNameEntry &Entry = Prologue.FileNames[FileIndex - 1]; + if (Entry.HasSource) + return Entry.Source; + else + return None; +} + bool DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, const char *CompDir, FileLineInfoKind Kind, @@ -944,5 +973,6 @@ Result.Line = Row.Line; Result.Column = Row.Column; Result.Discriminator = Row.Discriminator; + Result.Source = getSourceByIndex(Row.File, Kind); return true; } Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -1716,6 +1716,8 @@ /* ShouldSkipEmpty */ false); Printer.printChecksumKind(N); Printer.printString("checksum", N->getChecksum(), /* ShouldSkipEmpty */ true); + Printer.printString("source", N->getSource().getValueOr(StringRef()), + /* ShouldSkipEmpty */ true); Out << ")"; } Index: lib/IR/DIBuilder.cpp =================================================================== --- lib/IR/DIBuilder.cpp +++ lib/IR/DIBuilder.cpp @@ -14,6 +14,7 @@ #include "llvm/IR/DIBuilder.h" #include "llvm/IR/IRBuilder.h" #include "LLVMContextImpl.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/IR/Constants.h" @@ -204,8 +205,9 @@ } DIFile *DIBuilder::createFile(StringRef Filename, StringRef Directory, - DIFile::ChecksumKind CSKind, StringRef Checksum) { - return DIFile::get(VMContext, Filename, Directory, CSKind, Checksum); + DIFile::ChecksumKind CSKind, StringRef Checksum, + Optional Source) { + return DIFile::get(VMContext, Filename, Directory, CSKind, Checksum, Source); } DIMacro *DIBuilder::createMacro(DIMacroFile *Parent, unsigned LineNumber, Index: lib/IR/DebugInfo.cpp =================================================================== --- lib/IR/DebugInfo.cpp +++ lib/IR/DebugInfo.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -741,9 +742,16 @@ LLVMMetadataRef LLVMDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename, size_t FilenameLen, const char *Directory, - size_t DirectoryLen) { + size_t DirectoryLen, LLVMBool SourcePresent, + const char *Source, size_t SourceLen) { + Optional OptSource; + if (SourcePresent) + OptSource = StringRef(Source, SourceLen); return wrap(unwrap(Builder)->createFile(StringRef(Filename, FilenameLen), - StringRef(Directory, DirectoryLen))); + StringRef(Directory, DirectoryLen), + DIFile::CSK_None, + StringRef(), + OptSource)); } LLVMMetadataRef Index: lib/IR/DebugInfoMetadata.cpp =================================================================== --- lib/IR/DebugInfoMetadata.cpp +++ lib/IR/DebugInfoMetadata.cpp @@ -412,14 +412,15 @@ DIFile *DIFile::getImpl(LLVMContext &Context, MDString *Filename, MDString *Directory, DIFile::ChecksumKind CSKind, - MDString *Checksum, StorageType Storage, - bool ShouldCreate) { + MDString *Checksum, Optional Source, + StorageType Storage, bool ShouldCreate) { assert(isCanonical(Filename) && "Expected canonical MDString"); assert(isCanonical(Directory) && "Expected canonical MDString"); assert(isCanonical(Checksum) && "Expected canonical MDString"); - DEFINE_GETIMPL_LOOKUP(DIFile, (Filename, Directory, CSKind, Checksum)); - Metadata *Ops[] = {Filename, Directory, Checksum}; - DEFINE_GETIMPL_STORE(DIFile, (CSKind), Ops); + assert((!Source || isCanonical(*Source)) && "Expected canonical MDString"); + DEFINE_GETIMPL_LOOKUP(DIFile, (Filename, Directory, CSKind, Checksum, Source)); + Metadata *Ops[] = {Filename, Directory, Checksum, Source.getValueOr(nullptr)}; + DEFINE_GETIMPL_STORE(DIFile, (CSKind, Source), Ops); } DICompileUnit *DICompileUnit::getImpl( Index: lib/IR/LLVMContextImpl.h =================================================================== --- lib/IR/LLVMContextImpl.h +++ lib/IR/LLVMContextImpl.h @@ -568,24 +568,30 @@ MDString *Directory; DIFile::ChecksumKind CSKind; MDString *Checksum; + Optional Source; MDNodeKeyImpl(MDString *Filename, MDString *Directory, - DIFile::ChecksumKind CSKind, MDString *Checksum) - : Filename(Filename), Directory(Directory), CSKind(CSKind), - Checksum(Checksum) {} + DIFile::ChecksumKind CSKind, MDString *Checksum, + Optional Source) + : Filename(Filename), Directory(Directory), + CSKind(CSKind), Checksum(Checksum), Source(Source) {} MDNodeKeyImpl(const DIFile *N) : Filename(N->getRawFilename()), Directory(N->getRawDirectory()), - CSKind(N->getChecksumKind()), Checksum(N->getRawChecksum()) {} + CSKind(N->getChecksumKind()), Checksum(N->getRawChecksum()), + Source(N->getRawSource()) {} bool isKeyOf(const DIFile *RHS) const { return Filename == RHS->getRawFilename() && Directory == RHS->getRawDirectory() && CSKind == RHS->getChecksumKind() && - Checksum == RHS->getRawChecksum(); + Checksum == RHS->getRawChecksum() && + Source == RHS->getRawSource(); } unsigned getHashValue() const { - return hash_combine(Filename, Directory, CSKind, Checksum); + // TODO: How to involve the option in the hash? + return hash_combine(Filename, Directory, CSKind, Checksum, + Source.getValueOr(nullptr)); } }; Index: lib/MC/MCAsmStreamer.cpp =================================================================== --- lib/MC/MCAsmStreamer.cpp +++ lib/MC/MCAsmStreamer.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -214,6 +216,7 @@ unsigned EmitDwarfFileDirective(unsigned FileNo, StringRef Directory, StringRef Filename, MD5::MD5Result *Checksum = 0, + Optional Source = None, unsigned CUID = 0) override; void EmitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags, @@ -1077,12 +1080,13 @@ StringRef Directory, StringRef Filename, MD5::MD5Result *Checksum, + Optional Source, unsigned CUID) { assert(CUID == 0); MCDwarfLineTable &Table = getContext().getMCDwarfLineTable(CUID); unsigned NumFiles = Table.getMCDwarfFiles().size(); - FileNo = Table.getFile(Directory, Filename, Checksum, FileNo); + FileNo = Table.getFile(Directory, Filename, Checksum, Source, FileNo); if (FileNo == 0) return 0; if (NumFiles == Table.getMCDwarfFiles().size()) @@ -1113,6 +1117,10 @@ OS1 << " md5 "; PrintQuotedString(Checksum->digest(), OS1); } + if (Source) { + OS1 << " source "; + PrintQuotedString(*Source, OS1); + } if (MCTargetStreamer *TS = getTargetStreamer()) { TS->emitDwarfFileDirective(OS1.str()); } else { Index: lib/MC/MCContext.cpp =================================================================== --- lib/MC/MCContext.cpp +++ lib/MC/MCContext.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/MC/MCContext.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -532,15 +533,15 @@ // Dwarf Management //===----------------------------------------------------------------------===// -/// getDwarfFile - takes a file name an number to place in the dwarf file and +/// getDwarfFile - takes a file name and number to place in the dwarf file and /// directory tables. If the file number has already been allocated it is an /// error and zero is returned and the client reports the error, else the /// allocated file number is returned. The file numbers may be in any order. unsigned MCContext::getDwarfFile(StringRef Directory, StringRef FileName, unsigned FileNumber, MD5::MD5Result *Checksum, - unsigned CUID) { + Optional Source, unsigned CUID) { MCDwarfLineTable &Table = MCDwarfLineTablesCUMap[CUID]; - return Table.getFile(Directory, FileName, Checksum, FileNumber); + return Table.getFile(Directory, FileName, Checksum, Source, FileNumber); } /// isValidDwarfFileNumber - takes a dwarf file number and returns true if it Index: lib/MC/MCDwarf.cpp =================================================================== --- lib/MC/MCDwarf.cpp +++ lib/MC/MCDwarf.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -293,8 +294,13 @@ // The file format, which is the inline null-terminated filename and a // directory index. We don't track file size/timestamp so don't emit them - // in the v5 table. Emit MD5 checksums if we have them. - MCOS->EmitIntValue(HasMD5 ? 3 : 2, 1); + // in the v5 table. Emit MD5 checksums and source if we have them. + uint64_t Entries = 2; + if (HasMD5) + Entries += 1; + if (HasSource) + Entries += 2; + MCOS->EmitIntValue(Entries, 1); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path); MCOS->EmitULEB128IntValue(dwarf::DW_FORM_string); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_directory_index); @@ -303,6 +309,12 @@ MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_MD5); MCOS->EmitULEB128IntValue(dwarf::DW_FORM_data16); } + if (HasSource) { + MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_has_source); + MCOS->EmitULEB128IntValue(dwarf::DW_FORM_flag); + MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_source); + MCOS->EmitULEB128IntValue(dwarf::DW_FORM_string); + } // Then the list of file names. These start at 1. MCOS->EmitULEB128IntValue(MCDwarfFiles.size() - 1); for (unsigned i = 1; i < MCDwarfFiles.size(); ++i) { @@ -316,6 +328,11 @@ StringRef(reinterpret_cast(Cksum->Bytes.data()), Cksum->Bytes.size())); } + if (HasSource) { + MCOS->EmitIntValue(MCDwarfFiles[i].Source ? 1 : 0, 1); + MCOS->EmitBytes(MCDwarfFiles[i].Source.getValueOr(StringRef())); // Source and... + MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. + } } } @@ -412,13 +429,15 @@ unsigned MCDwarfLineTable::getFile(StringRef &Directory, StringRef &FileName, MD5::MD5Result *Checksum, + Optional &Source, unsigned FileNumber) { - return Header.getFile(Directory, FileName, Checksum, FileNumber); + return Header.getFile(Directory, FileName, Checksum, Source, FileNumber); } unsigned MCDwarfLineTableHeader::getFile(StringRef &Directory, StringRef &FileName, MD5::MD5Result *Checksum, + Optional &Source, unsigned FileNumber) { if (Directory == CompilationDir) Directory = ""; @@ -489,6 +508,9 @@ File.Checksum = Checksum; if (Checksum) HasMD5 = true; + File.Source = Source; + if (Source) + HasSource = true; // return the allocated FileNumber. return FileNumber; Index: lib/MC/MCParser/AsmParser.cpp =================================================================== --- lib/MC/MCParser/AsmParser.cpp +++ lib/MC/MCParser/AsmParser.cpp @@ -3296,7 +3296,7 @@ /// parseDirectiveFile /// ::= .file filename -/// ::= .file number [directory] filename [md5 checksum] +/// ::= .file number [directory] filename [md5 checksum] [source source-text] bool AsmParser::parseDirectiveFile(SMLoc DirectiveLoc) { // FIXME: I'm not sure what this is. int64_t FileNumber = -1; @@ -3333,23 +3333,36 @@ } std::string Checksum; - if (!parseOptionalToken(AsmToken::EndOfStatement)) { + + Optional Source; + bool HasSource = false; + std::string SourceString; + + while (!parseOptionalToken(AsmToken::EndOfStatement)) { StringRef Keyword; if (check(getTok().isNot(AsmToken::Identifier), "unexpected token in '.file' directive") || - parseIdentifier(Keyword) || - check(Keyword != "md5", "unexpected token in '.file' directive")) - return true; - if (getLexer().is(AsmToken::String) && - check(FileNumber == -1, "MD5 checksum specified, but no file number")) - return true; - if (check(getTok().isNot(AsmToken::String), - "unexpected token in '.file' directive") || - parseEscapedString(Checksum) || - check(Checksum.size() != 32, "invalid MD5 checksum specified") || - parseToken(AsmToken::EndOfStatement, - "unexpected token in '.file' directive")) + parseIdentifier(Keyword)) return true; + if (Keyword == "md5") { + if (check(FileNumber == -1, + "MD5 checksum specified, but no file number") || + check(getTok().isNot(AsmToken::String), + "unexpected token in '.file' directive") || + parseEscapedString(Checksum) || + check(Checksum.size() != 32, "invalid MD5 checksum specified")) + return true; + } else if (Keyword == "source") { + HasSource = true; + if (check(FileNumber == -1, + "source specified, but no file number") || + check(getTok().isNot(AsmToken::String), + "unexpected token in '.file' directive") || + parseEscapedString(SourceString)) + return true; + } else { + return TokError("unexpected token in '.file' directive"); + } } if (FileNumber == -1) @@ -3363,12 +3376,16 @@ CKMem = (MD5::MD5Result *)Ctx.allocate(sizeof(MD5::MD5Result), 1); memcpy(&CKMem->Bytes, Checksum.data(), 16); } + if (HasSource) { + char *SourceBuf = static_cast(Ctx.allocate(SourceString.size())); + memcpy(SourceBuf, SourceString.data(), SourceString.size()); + Source = StringRef(SourceBuf, SourceString.size()); + } // If there is -g option as well as debug info from directive file, // we turn off -g option, directly use the existing debug info instead. if (getContext().getGenDwarfForAssembly()) getContext().setGenDwarfForAssembly(false); - else if (getStreamer().EmitDwarfFileDirective(FileNumber, Directory, - Filename, CKMem) == 0) + else if (getStreamer().EmitDwarfFileDirective(FileNumber, Directory, Filename, CKMem, Source) == 0) return Error(FileNumberLoc, "file number already allocated"); } Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/MC/MCStreamer.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -196,8 +197,9 @@ StringRef Directory, StringRef Filename, MD5::MD5Result *Checksum, + Optional Source, unsigned CUID) { - return getContext().getDwarfFile(Directory, Filename, FileNo, Checksum, CUID); + return getContext().getDwarfFile(Directory, Filename, FileNo, Checksum, Source, CUID); } void MCStreamer::EmitDwarfLocDirective(unsigned FileNo, unsigned Line, Index: test/Assembler/debug-info.ll =================================================================== --- test/Assembler/debug-info.ll +++ test/Assembler/debug-info.ll @@ -1,8 +1,8 @@ ; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s ; RUN: verify-uselistorder %s -; CHECK: !named = !{!0, !0, !1, !2, !3, !4, !5, !6, !7, !8, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !27, !28, !29, !30, !31, !32, !33, !33} -!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37} +; CHECK: !named = !{!0, !0, !1, !2, !3, !4, !5, !6, !7, !8, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !27, !28, !29, !30, !31, !32, !33, !33, !34, !35} +!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39} ; CHECK: !0 = !DISubrange(count: 3) ; CHECK-NEXT: !1 = !DISubrange(count: 3, lowerBound: 4) @@ -85,3 +85,8 @@ !35 = !DIFile(filename: "file", directory: "dir", checksumkind: CSK_MD5, checksum: "000102030405060708090a0b0c0d0e0f") !36 = !DIFile(filename: "file", directory: "dir", checksumkind: CSK_None) !37 = !DIFile(filename: "file", directory: "dir", checksumkind: CSK_None, checksum: "") + +; CHECK-NEXT: !34 = !DIFile(filename: "file", directory: "dir", source: "int source() { }\0A") +; CHECK-NEXT: !35 = !DIFile(filename: "file", directory: "dir", checksumkind: CSK_MD5, checksum: "3a420e2646916a475e68de8d48f779f5", source: "int source() { }\0A") +!38 = !DIFile(filename: "file", directory: "dir", source: "int source() { }\0A") +!39 = !DIFile(filename: "file", directory: "dir", checksumkind: CSK_MD5, checksum: "3a420e2646916a475e68de8d48f779f5", source: "int source() { }\0A") Index: test/Bindings/llvm-c/debug_info.ll =================================================================== --- test/Bindings/llvm-c/debug_info.ll +++ test/Bindings/llvm-c/debug_info.ll @@ -5,4 +5,4 @@ ; CHECK: !llvm.dbg.cu = !{!0} ; CHECK: !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "llvm-c-test", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false) -; CHECK-NEXT: !1 = !DIFile(filename: "debuginfo.c\00", directory: ".") +; CHECK-NEXT: !1 = !DIFile(filename: "debuginfo.c\00", directory: ".", source: "source\00") Index: test/Bitcode/metadata-source.ll =================================================================== --- /dev/null +++ test/Bitcode/metadata-source.ll @@ -0,0 +1,23 @@ +; RUN: llvm-dis < %s.bc | FileCheck %s +; RUN: verify-uselistorder < %s.bc + +@a = common global i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ c12b573f9ac61655cce52628b34235f58edaf984) (https://scott.linder@llvm.org/git/llvm.git 90c4822e8541eb07891cd03e614c530c30f8aa12)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) + +; CHECK: !3 = !DIFile(filename: "a.c", directory: "/home/slinder1/test/link", source: "int a;\0A") + +!3 = !DIFile(filename: "a.c", directory: "/home/slinder1/test/link", source: "int a;\0A") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ c12b573f9ac61655cce52628b34235f58edaf984) (https://scott.linder@llvm.org/git/llvm.git 90c4822e8541eb07891cd03e614c530c30f8aa12)"} Index: test/CodeGen/Generic/dwarf-source.ll =================================================================== --- /dev/null +++ test/CodeGen/Generic/dwarf-source.ll @@ -0,0 +1,55 @@ +; Source text provided by IR should be passed through to asm. +; It is emitted to an object file only for DWARF 5 or later. + +; Darwin clamps the line table at DWARF v2 so XFAIL this test. +; XFAIL: darwin + +; REQUIRES: object-emission +; RUN: %llc_dwarf -dwarf-version 4 -filetype=asm -o - %s | FileCheck %s --check-prefix=ASM +; RUN: %llc_dwarf -dwarf-version 5 -filetype=asm -o - %s | FileCheck %s --check-prefix=ASM +; RUN: %llc_dwarf -dwarf-version 4 -filetype=obj -o %t4.o %s +; RUN: llvm-dwarfdump -debug-line %t4.o | FileCheck %s --check-prefix=OBJ-4 +; RUN: %llc_dwarf -dwarf-version 5 -filetype=obj -o %t5.o %s +; RUN: llvm-dwarfdump -debug-line %t5.o | FileCheck %s --check-prefix=OBJ-5 + +; FIXME: Need to convey the MD5 for the primary source file. +; ASM: .file 1 ".{{/|\\\\}}t1.h" source "11111111111111111111111111111111" +; ASM: .file 2 ".{{/|\\\\}}t2.h" source "22222222222222222222222222222222" + +; OBJ-4: Dir Mod Time File Len File Name +; OBJ-4: file_names[ 1] 1 0x00000000 0x00000000 t1.h +; OBJ-4-NOT: 11111111111111111111111111111111 +; OBJ-4: file_names[ 2] 1 0x00000000 0x00000000 t2.h +; OBJ-4-NOT: 22222222222222222222222222222222 + +; OBJ-5: Dir Mod Time File Len File Name +; OBJ-5: file_names[ 1] 1 0x00000000 0x00000000 t1.h +; OBJ-5-NEXT: 11111111111111111111111111111111 +; OBJ-5: file_names[ 2] 1 0x00000000 0x00000000 t2.h +; OBJ-5-NEXT: 22222222222222222222222222222222 + +; ModuleID = 't.c' +source_filename = "t.c" + +@t1 = global i32 1, align 4, !dbg !0 +@t2 = global i32 0, align 4, !dbg !6 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!11, !12, !13} +!llvm.ident = !{!14} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "t1", scope: !2, file: !10, line: 1, type: !9, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0 (trunk 322159)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) +!3 = !DIFile(filename: "t.c", directory: "/home/probinson/projects/scratch", source: "00000000000000000000000000000000") +!4 = !{} +!5 = !{!0, !6} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "t2", scope: !2, file: !8, line: 1, type: !9, isLocal: false, isDefinition: true) +!8 = !DIFile(filename: "./t2.h", directory: "/home/probinson/projects/scratch", source: "22222222222222222222222222222222") +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!10 = !DIFile(filename: "./t1.h", directory: "/home/probinson/projects/scratch", source: "11111111111111111111111111111111") +!11 = !{i32 2, !"Dwarf Version", i32 4} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!14 = !{!"clang version 7.0.0 (trunk 322159)"} Index: test/Linker/Inputs/metadata-source-a.ll =================================================================== --- /dev/null +++ test/Linker/Inputs/metadata-source-a.ll @@ -0,0 +1,22 @@ +; ModuleID = 'a.c' +source_filename = "a.c" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@a = common global i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ c12b573f9ac61655cce52628b34235f58edaf984) (https://scott.linder@llvm.org/git/llvm.git 90c4822e8541eb07891cd03e614c530c30f8aa12)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) +!3 = !DIFile(filename: "a.c", directory: "/home/slinder1/test/link", source: "int a;\0A") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ c12b573f9ac61655cce52628b34235f58edaf984) (https://scott.linder@llvm.org/git/llvm.git 90c4822e8541eb07891cd03e614c530c30f8aa12)"} Index: test/Linker/Inputs/metadata-source-b.ll =================================================================== --- /dev/null +++ test/Linker/Inputs/metadata-source-b.ll @@ -0,0 +1,22 @@ +; ModuleID = 'b.c' +source_filename = "b.c" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@b = common global i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ c12b573f9ac61655cce52628b34235f58edaf984) (https://scott.linder@llvm.org/git/llvm.git 90c4822e8541eb07891cd03e614c530c30f8aa12)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) +!3 = !DIFile(filename: "b.c", directory: "/home/slinder1/test/link", source: "int b;\0A") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ c12b573f9ac61655cce52628b34235f58edaf984) (https://scott.linder@llvm.org/git/llvm.git 90c4822e8541eb07891cd03e614c530c30f8aa12)"} Index: test/Linker/metadata-source.ll =================================================================== --- /dev/null +++ test/Linker/metadata-source.ll @@ -0,0 +1,4 @@ +; RUN: llvm-link %p/Inputs/metadata-source-a.ll %p/Inputs/metadata-source-b.ll -S | FileCheck %s + +; CHECK: !DIFile(filename: "a.c", directory: "/home/slinder1/test/link", source: "int a;\0A") +; CHECK: !DIFile(filename: "b.c", directory: "/home/slinder1/test/link", source: "int b;\0A") Index: test/MC/AsmParser/debug-empty-source.s =================================================================== --- /dev/null +++ test/MC/AsmParser/debug-empty-source.s @@ -0,0 +1,7 @@ +// RUN: llvm-mc %s -o -| FileCheck %s + +.file 1 "dir1" "foo" source "" +.loc 1 1 0 +nop + +# CHECK: .file {{.*}} source "" Index: test/MC/AsmParser/debug-no-source.s =================================================================== --- /dev/null +++ test/MC/AsmParser/debug-no-source.s @@ -0,0 +1,5 @@ +// RUN: llvm-mc %s | FileCheck %s + +.file 1 "dir1/foo" + +# CHECK-NOT: .file {{.*}} source Index: test/MC/ELF/debug-md5-and-source.s =================================================================== --- /dev/null +++ test/MC/ELF/debug-md5-and-source.s @@ -0,0 +1,20 @@ +// RUN: llvm-mc -triple x86_64-unknown-unknown -dwarf-version 5 -filetype=obj %s -o -| llvm-dwarfdump --debug-line - | FileCheck %s + + .file 1 "dir1/foo" md5 "ee87e05688663173cd6043a3a15bba6e" source "void foo() {}" + .file 2 "dir2/bar" source "void bar() {}" md5 "816225a0c90ca8948b70eb58be4d522f" + .loc 1 1 0 + nop + .loc 2 1 0 + nop + +# CHECK: debug_line[0x00000000] +# CHECK: version: 5 +# CHECK: include_directories[ 0] = '' +# CHECK: include_directories[ 1] = 'dir1' +# CHECK: include_directories[ 2] = 'dir2' +# CHECK-NOT: include_directories +# CHECK: Dir MD5 Checksum File Name +# CHECK: file_names[ 1] 1 ee87e05688663173cd6043a3a15bba6e foo +# CHECK-NEXT: void foo() {} +# CHECK: file_names[ 2] 2 816225a0c90ca8948b70eb58be4d522f bar +# CHECK-NEXT: void bar() {} Index: test/MC/ELF/debug-source.s =================================================================== --- /dev/null +++ test/MC/ELF/debug-source.s @@ -0,0 +1,22 @@ +// RUN: llvm-mc -triple x86_64-unknown-unknown -dwarf-version 5 -filetype=obj %s -o -| llvm-dwarfdump --debug-line - | FileCheck %s + + .file 1 "dir1/foo" source "void foo() {}" + .file 2 "dir2" "bar" source "void bar()\n{\n}" + .loc 1 1 0 + nop + .loc 2 1 0 + nop + +# CHECK: debug_line[0x00000000] +# CHECK: version: 5 +# CHECK: include_directories[ 0] = '' +# CHECK: include_directories[ 1] = 'dir1' +# CHECK: include_directories[ 2] = 'dir2' +# CHECK-NOT: include_directories +# CHECK: Dir Mod Time File Len File Name +# CHECK: file_names[ 1] 1 0x00000000 0x00000000 foo +# CHECK-NEXT: void foo() {} +# CHECK: file_names[ 2] 2 0x00000000 0x00000000 bar +# CHECK-NEXT: void bar() +# CHECK-NEXT: { +# CHECK-NEXT: } Index: test/tools/llvm-objdump/embedded-source.test =================================================================== --- /dev/null +++ test/tools/llvm-objdump/embedded-source.test @@ -0,0 +1,22 @@ +; RUN: llvm-objdump -disassemble -line-numbers %p/Inputs/embedded-source | FileCheck --check-prefix=LINE %s +; RUN: llvm-objdump -disassemble -source %p/Inputs/embedded-source | FileCheck --check-prefix=SOURCE %s + +; LINE: main: +; LINE-NEXT: ; {{.*}}embedded-source.c:1 +; LINE-NEXT: pushq %rbp +; LINE: ; {{.*}}embedded-source.c:2 +; LINE-NEXT: movl $2 +; LINE: ; {{.*}}embedded-source.c:3 +; LINE: addl $1 +; LINE: ; {{.*}}embedded-source.c:4 +; LINE: retq + +; SOURCE: main: +; SOURCE-NEXT: ; int main(int argc, char *argv[]) { +; SOURCE-NEXT: pushq %rbp +; SOURCE: ; int i = 2; +; SOURCE-NEXT: movl $2 +; SOURCE: ; i += 1; +; SOURCE: addl $1 +; SOURCE: ; return i; +; SOURCE: retq Index: tools/llvm-c-test/debuginfo.c =================================================================== --- tools/llvm-c-test/debuginfo.c +++ tools/llvm-c-test/debuginfo.c @@ -19,7 +19,7 @@ LLVMDIBuilderRef DIB = LLVMCreateDIBuilder(M); LLVMMetadataRef File = LLVMDIBuilderCreateFile(DIB, "debuginfo.c", 12, - ".", 1); + ".", 1, 1, "source", 7); LLVMDIBuilderCreateCompileUnit(DIB, LLVMDWARFSourceLanguageC, File,"llvm-c-test", 11, 0, NULL, 0, 0, Index: tools/llvm-objdump/llvm-objdump.cpp =================================================================== --- tools/llvm-objdump/llvm-objdump.cpp +++ tools/llvm-objdump/llvm-objdump.cpp @@ -408,7 +408,7 @@ std::unordered_map> LineCache; private: - bool cacheSource(const std::string& File); + bool cacheSource(const DILineInfo& LineInfoFile); public: SourcePrinter() = default; @@ -423,23 +423,29 @@ StringRef Delimiter = "; "); }; -bool SourcePrinter::cacheSource(const std::string& File) { - auto BufferOrError = MemoryBuffer::getFile(File); - if (!BufferOrError) - return false; +bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { + std::unique_ptr Buffer; + if (LineInfo.Source) { + Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); + } else { + auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); + if (!BufferOrError) + return false; + Buffer = std::move(*BufferOrError); + } // Chomp the file to get lines - size_t BufferSize = (*BufferOrError)->getBufferSize(); - const char *BufferStart = (*BufferOrError)->getBufferStart(); + size_t BufferSize = Buffer->getBufferSize(); + const char *BufferStart = Buffer->getBufferStart(); for (const char *Start = BufferStart, *End = BufferStart; End < BufferStart + BufferSize; End++) if (*End == '\n' || End == BufferStart + BufferSize - 1 || (*End == '\r' && *(End + 1) == '\n')) { - LineCache[File].push_back(StringRef(Start, End - Start)); + LineCache[LineInfo.FileName].push_back(StringRef(Start, End - Start)); if (*End == '\r') End++; Start = End + 1; } - SourceCache[File] = std::move(*BufferOrError); + SourceCache[LineInfo.FileName] = std::move(Buffer); return true; } @@ -463,7 +469,7 @@ OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; if (PrintSource) { if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo.FileName)) + if (!cacheSource(LineInfo)) return; auto FileBuffer = SourceCache.find(LineInfo.FileName); if (FileBuffer != SourceCache.end()) {