Skip to content

Commit

Permalink
[DebugInfo] Support DWARF v5 source code embedding extension
Browse files Browse the repository at this point in the history
In DWARF v5 the Line Number Program Header is extensible, allowing values with
new content types. In this extension a content type is added,
DW_LNCT_LLVM_source, which contains the embedded source code of the file.

Add new optional attribute for !DIFile IR metadata called source which contains
source text. Use this to output the source to the DWARF line table of code
objects. Analogously extend METADATA_FILE in Bitcode and .file directive in ASM
to support optional source.

Teach llvm-dwarfdump and llvm-objdump about the new values. Update the output
format of llvm-dwarfdump to make room for the new attribute on file_names
entries, and support embedded sources for the -source option in llvm-objdump.

Differential Revision: https://reviews.llvm.org/D42765

llvm-svn: 325970
  • Loading branch information
scott-linder committed Feb 23, 2018
1 parent 5e2f6ba commit 16c7bda
Show file tree
Hide file tree
Showing 51 changed files with 581 additions and 221 deletions.
58 changes: 54 additions & 4 deletions llvm/docs/AMDGPUUsage.rst
Expand Up @@ -780,7 +780,7 @@ The following relocation types are supported:
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.

Expand Down Expand Up @@ -836,10 +836,60 @@ Register Mapping
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 DWARF v5 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 the embedded source DWARF v5 extension.
``-gno-embed-source``
Disable the embedded source DWARF v5 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 one extended content types in the DWARF v5 Line Number
Program Header, which is used to encode embedded source.

.. table:: AMDGPU DWARF Line Number Program Header Extended Content Types
:name: amdgpu-dwarf-extended-content-types

============================ ======================
Content Type Form
============================ ======================
``DW_LNCT_LLVM_source`` ``DW_FORM_line_strp``
============================ ======================

The source field will contain the UTF-8 encoded, null-terminated source text
with ``'\n'`` line endings. When the source field is present, consumers can use
the embedded source instead of attempting to discover the source on disk. When
the source field is absent, consumers can access the file to get the source
text.

The above content type appears in the ``file_name_entry_format`` field of the
line table prologue, and its corresponding value appear in the ``file_names``
field. The current encoding of the content type 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_LLVM_source`` 0x2001
============================ ====================

.. _amdgpu-code-conventions:

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Expand Up @@ -747,6 +747,9 @@ HANDLE_DW_LNCT(0x02, directory_index)
HANDLE_DW_LNCT(0x03, timestamp)
HANDLE_DW_LNCT(0x04, size)
HANDLE_DW_LNCT(0x05, MD5)
// A vendor extension until http://dwarfstd.org/ShowIssue.php?issue=180201.1 is
// accepted and incorporated into the next DWARF standard.
HANDLE_DW_LNCT(0x2001, LLVM_source)

// DWARF v5 Macro information.
HANDLE_DW_MACRO(0x01, define)
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/DebugInfo/DIContext.h
Expand Up @@ -31,6 +31,7 @@ namespace llvm {
struct DILineInfo {
std::string FileName;
std::string FunctionName;
Optional<StringRef> Source;
uint32_t Line = 0;
uint32_t Column = 0;
uint32_t StartLine = 0;
Expand Down
26 changes: 24 additions & 2 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
Expand Up @@ -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"
Expand All @@ -36,6 +37,25 @@ class DWARFDebugLine {
uint64_t ModTime = 0;
uint64_t Length = 0;
MD5::MD5Result Checksum;
DWARFFormValue Source;
};

/// Tracks which optional content types are present in a DWARF file name
/// entry format.
struct ContentTypeTracker {
ContentTypeTracker() = default;

/// Whether filename entries provide a modification timestamp.
bool HasModTime = false;
/// Whether filename entries provide a file size.
bool HasLength = false;
/// For v5, whether filename entries provide an MD5 checksum.
bool HasMD5 = false;
/// For v5, whether filename entries provide source text.
bool HasSource = false;

/// Update tracked content types with \p ContentType.
void trackContentType(dwarf::LineNumberEntryFormat ContentType);
};

struct Prologue {
Expand Down Expand Up @@ -68,8 +88,8 @@ class DWARFDebugLine {
uint8_t LineRange;
/// The number assigned to the first special opcode.
uint8_t OpcodeBase;
/// For v5, whether filename entries provide an MD5 checksum.
bool HasMD5;
/// This tracks which optional file format content types are present.
ContentTypeTracker ContentTypes;
std::vector<uint8_t> StandardOpcodeLengths;
std::vector<DWARFFormValue> IncludeDirectories;
std::vector<FileNameEntry> FileNames;
Expand Down Expand Up @@ -239,6 +259,8 @@ class DWARFDebugLine {
private:
uint32_t findRowInSeq(const DWARFDebugLine::Sequence &Seq,
uint64_t Address) const;
Optional<StringRef> getSourceByIndex(uint64_t FileIndex,
DILineInfoSpecifier::FileLineInfoKind Kind) const;
};

const LineTable *getLineTable(uint32_t Offset) const;
Expand Down
9 changes: 6 additions & 3 deletions llvm/include/llvm/IR/DIBuilder.h
Expand Up @@ -18,7 +18,7 @@
#include "llvm/ADT/ArrayRef.h"
#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"
Expand Down Expand Up @@ -141,10 +141,13 @@ namespace llvm {
/// Create a file descriptor to hold debugging information for a file.
/// \param Filename File name.
/// \param Directory Directory.
/// \param Checksum Checksum kind (e.g. CSK_MD5, CSK_SHA1, etc.) and value.
/// \param Checksum Optional checksum kind (e.g. CSK_MD5, CSK_SHA1, etc.)
/// and value.
/// \param Source Optional source text.
DIFile *
createFile(StringRef Filename, StringRef Directory,
Optional<DIFile::ChecksumInfo<StringRef>> Checksum = None);
Optional<DIFile::ChecksumInfo<StringRef>> Checksum = None,
Optional<StringRef> Source = None);

/// Create debugging information entry for a macro.
/// \param Parent Macro parent (could be nullptr).
Expand Down
46 changes: 38 additions & 8 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Expand Up @@ -455,6 +455,7 @@ class DIScope : public DINode {

inline StringRef getFilename() const;
inline StringRef getDirectory() const;
inline Optional<StringRef> getSource() const;

StringRef getName() const;
DIScopeRef getScope() const;
Expand Down Expand Up @@ -532,42 +533,48 @@ class DIFile : public DIScope {

private:
Optional<ChecksumInfo<MDString *>> Checksum;
Optional<MDString *> Source;

DIFile(LLVMContext &C, StorageType Storage,
Optional<ChecksumInfo<MDString *>> CS,
Optional<ChecksumInfo<MDString *>> CS, Optional<MDString *> Src,
ArrayRef<Metadata *> Ops)
: DIScope(C, DIFileKind, Storage, dwarf::DW_TAG_file_type, Ops),
Checksum(CS) {}
Checksum(CS), Source(Src) {}
~DIFile() = default;

static DIFile *getImpl(LLVMContext &Context, StringRef Filename,
StringRef Directory,
Optional<ChecksumInfo<StringRef>> CS,
Optional<StringRef> Source,
StorageType Storage, bool ShouldCreate = true) {
Optional<ChecksumInfo<MDString *>> MDChecksum;
if (CS)
MDChecksum.emplace(CS->Kind, getCanonicalMDString(Context, CS->Value));
return getImpl(Context, getCanonicalMDString(Context, Filename),
getCanonicalMDString(Context, Directory), MDChecksum,
Source ? Optional<MDString *>(getCanonicalMDString(Context, *Source)) : None,
Storage, ShouldCreate);
}
static DIFile *getImpl(LLVMContext &Context, MDString *Filename,
MDString *Directory,
Optional<ChecksumInfo<MDString *>> CS,
StorageType Storage, bool ShouldCreate = true);
Optional<MDString *> Source, StorageType Storage,
bool ShouldCreate = true);

TempDIFile cloneImpl() const {
return getTemporary(getContext(), getFilename(), getDirectory(),
getChecksum());
getChecksum(), getSource());
}

public:
DEFINE_MDNODE_GET(DIFile, (StringRef Filename, StringRef Directory,
Optional<ChecksumInfo<StringRef>> CS = None),
(Filename, Directory, CS))
Optional<ChecksumInfo<StringRef>> CS = None,
Optional<StringRef> Source = None),
(Filename, Directory, CS, Source))
DEFINE_MDNODE_GET(DIFile, (MDString * Filename, MDString *Directory,
Optional<ChecksumInfo<MDString *>> CS = None),
(Filename, Directory, CS))
Optional<ChecksumInfo<MDString *>> CS = None,
Optional<MDString *> Source = None),
(Filename, Directory, CS, Source))

TempDIFile clone() const { return cloneImpl(); }

Expand All @@ -579,10 +586,14 @@ class DIFile : public DIScope {
StringRefChecksum.emplace(Checksum->Kind, Checksum->Value->getString());
return StringRefChecksum;
}
Optional<StringRef> getSource() const {
return Source ? Optional<StringRef>((*Source)->getString()) : None;
}

MDString *getRawFilename() const { return getOperandAs<MDString>(0); }
MDString *getRawDirectory() const { return getOperandAs<MDString>(1); }
Optional<ChecksumInfo<MDString *>> getRawChecksum() const { return Checksum; }
Optional<MDString *> getRawSource() const { return Source; }

static StringRef getChecksumKindAsString(ChecksumKind CSKind);
static Optional<ChecksumKind> getChecksumKind(StringRef CSKindStr);
Expand All @@ -604,6 +615,12 @@ StringRef DIScope::getDirectory() const {
return "";
}

Optional<StringRef> 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
Expand Down Expand Up @@ -1408,6 +1425,7 @@ class DILocation : public MDNode {
DIFile *getFile() const { return getScope()->getFile(); }
StringRef getFilename() const { return getScope()->getFilename(); }
StringRef getDirectory() const { return getScope()->getDirectory(); }
Optional<StringRef> getSource() const { return getScope()->getSource(); }

/// Get the scope where this is inlined.
///
Expand Down Expand Up @@ -2177,6 +2195,12 @@ class DIVariable : public DINode {
return "";
}

Optional<StringRef> getSource() const {
if (auto *F = getFile())
return F->getSource();
return None;
}

Metadata *getRawScope() const { return getOperand(0); }
MDString *getRawName() const { return getOperandAs<MDString>(1); }
Metadata *getRawFile() const { return getOperand(2); }
Expand Down Expand Up @@ -2624,6 +2648,12 @@ class DIObjCProperty : public DINode {
return "";
}

Optional<StringRef> getSource() const {
if (auto *F = getFile())
return F->getSource();
return None;
}

MDString *getRawName() const { return getOperandAs<MDString>(0); }
Metadata *getRawFile() const { return getOperand(1); }
MDString *getRawGetterName() const { return getOperandAs<MDString>(2); }
Expand Down
4 changes: 3 additions & 1 deletion llvm/include/llvm/MC/MCContext.h
Expand Up @@ -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"
Expand Down Expand Up @@ -496,7 +497,8 @@ namespace llvm {
/// Creates an entry in the dwarf file and directory tables.
Expected<unsigned> getDwarfFile(StringRef Directory, StringRef FileName,
unsigned FileNumber,
MD5::MD5Result *Checksum, unsigned CUID);
MD5::MD5Result *Checksum,
Optional<StringRef> Source, unsigned CUID);

bool isValidDwarfFileNumber(unsigned FileNumber, unsigned CUID = 0);

Expand Down
19 changes: 14 additions & 5 deletions llvm/include/llvm/MC/MCDwarf.h
Expand Up @@ -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"
Expand Down Expand Up @@ -48,7 +49,6 @@ class SourceMgr;
/// 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.
Expand All @@ -57,6 +57,10 @@ struct MCDwarfFile {
/// The MD5 checksum, if there is one. Non-owning pointer to data allocated
/// in MCContext.
MD5::MD5Result *Checksum = nullptr;

/// The source code of the file. Non-owning reference to data allocated in
/// MCContext.
Optional<StringRef> Source;
};

/// \brief Instances of this class represent the information from a
Expand Down Expand Up @@ -211,11 +215,13 @@ struct MCDwarfLineTableHeader {
StringMap<unsigned> SourceIdMap;
StringRef CompilationDir;
bool HasMD5 = false;
bool HasSource = false;

MCDwarfLineTableHeader() = default;

Expected<unsigned> tryGetFile(StringRef &Directory, StringRef &FileName,
MD5::MD5Result *Checksum,
Optional<StringRef> &Source,
unsigned FileNumber = 0);
std::pair<MCSymbol *, MCSymbol *>
Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
Expand All @@ -240,8 +246,8 @@ class MCDwarfDwoLineTable {
}

unsigned getFile(StringRef Directory, StringRef FileName,
MD5::MD5Result *Checksum) {
return cantFail(Header.tryGetFile(Directory, FileName, Checksum));
MD5::MD5Result *Checksum, Optional<StringRef> Source) {
return cantFail(Header.tryGetFile(Directory, FileName, Checksum, Source));
}

void Emit(MCStreamer &MCOS, MCDwarfLineTableParams Params) const;
Expand All @@ -261,10 +267,13 @@ class MCDwarfLineTable {

Expected<unsigned> tryGetFile(StringRef &Directory, StringRef &FileName,
MD5::MD5Result *Checksum,
Optional<StringRef> Source,
unsigned FileNumber = 0);
unsigned getFile(StringRef &Directory, StringRef &FileName,
MD5::MD5Result *Checksum, unsigned FileNumber = 0) {
return cantFail(tryGetFile(Directory, FileName, Checksum, FileNumber));
MD5::MD5Result *Checksum, Optional<StringRef> &Source,
unsigned FileNumber = 0) {
return cantFail(tryGetFile(Directory, FileName, Checksum, Source,
FileNumber));
}

MCSymbol *getLabel() const {
Expand Down

0 comments on commit 16c7bda

Please sign in to comment.