Add debug info support for Swift/Clang APINotes.

In order for dsymutil to collect .apinotes files (which capture
attributes such as nullability, Swift import names, and availability),
I want to propose adding an apinotes: field to DIModule that gets
translated into a DW_AT_LLVM_apinotes (path) nested inside
DW_TAG_module. This will be primarily used by LLDB to indirectly
extract the Swift names of Clang declarations that were deserialized
from DWARF.

<rdar://problem/59514626>

Differential Revision: https://reviews.llvm.org/D75585
This commit is contained in:
Adrian Prantl 2020-03-03 16:05:23 -08:00
parent 49d4e0e327
commit d5180ea134
16 changed files with 88 additions and 43 deletions

View File

@ -288,12 +288,15 @@ LLVMDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename,
* \param ConfigMacrosLen The length of the C string passed to \c ConfigMacros.
* \param IncludePath The path to the module map file.
* \param IncludePathLen The length of the C string passed to \c IncludePath.
* \param APINotesFile The path to an API notes file for the module.
* \param APINotesFileLen The length of the C string passed to \c APINotestFile.
*/
LLVMMetadataRef
LLVMDIBuilderCreateModule(LLVMDIBuilderRef Builder, LLVMMetadataRef ParentScope,
const char *Name, size_t NameLen,
const char *ConfigMacros, size_t ConfigMacrosLen,
const char *IncludePath, size_t IncludePathLen);
const char *IncludePath, size_t IncludePathLen,
const char *APINotestFile, size_t APINotestFileLen);
/**
* Creates a new descriptor for a namespace with the specified parent scope.

View File

@ -412,7 +412,11 @@ HANDLE_DW_AT(0x3e00, LLVM_include_path, 0, LLVM)
HANDLE_DW_AT(0x3e01, LLVM_config_macros, 0, LLVM)
HANDLE_DW_AT(0x3e02, LLVM_sysroot, 0, LLVM)
HANDLE_DW_AT(0x3e03, LLVM_tag_offset, 0, LLVM)
// The missing numbers here are reserved for ptrauth support.
HANDLE_DW_AT(0x3e07, LLVM_apinotes, 0, APPLE)
// Apple extensions.
HANDLE_DW_AT(0x3fe1, APPLE_optimized, 0, APPLE)
HANDLE_DW_AT(0x3fe2, APPLE_flags, 0, APPLE)
HANDLE_DW_AT(0x3fe3, APPLE_isa, 0, APPLE)

View File

@ -741,9 +741,10 @@ namespace llvm {
/// A space-separated shell-quoted list of -D macro
/// definitions as they would appear on a command line.
/// \param IncludePath The path to the module map file.
/// \param APINotesFile The path to an API notes file for this module.
DIModule *createModule(DIScope *Scope, StringRef Name,
StringRef ConfigurationMacros,
StringRef IncludePath);
StringRef IncludePath, StringRef APINotesFile = {});
/// This creates a descriptor for a lexical block with a new file
/// attached. This merely extends the existing

View File

@ -2089,31 +2089,38 @@ class DIModule : public DIScope {
static DIModule *getImpl(LLVMContext &Context, DIScope *Scope, StringRef Name,
StringRef ConfigurationMacros, StringRef IncludePath,
StorageType Storage, bool ShouldCreate = true) {
StringRef APINotesFile, StorageType Storage,
bool ShouldCreate = true) {
return getImpl(Context, Scope, getCanonicalMDString(Context, Name),
getCanonicalMDString(Context, ConfigurationMacros),
getCanonicalMDString(Context, IncludePath),
getCanonicalMDString(Context, APINotesFile),
Storage, ShouldCreate);
}
static DIModule *getImpl(LLVMContext &Context, Metadata *Scope,
MDString *Name, MDString *ConfigurationMacros,
MDString *IncludePath, StorageType Storage,
bool ShouldCreate = true);
MDString *IncludePath, MDString *APINotesFile,
StorageType Storage, bool ShouldCreate = true);
TempDIModule cloneImpl() const {
return getTemporary(getContext(), getScope(), getName(),
getConfigurationMacros(), getIncludePath());
getConfigurationMacros(), getIncludePath(),
getAPINotesFile());
}
public:
DEFINE_MDNODE_GET(DIModule,
(DIScope * Scope, StringRef Name,
StringRef ConfigurationMacros, StringRef IncludePath),
(Scope, Name, ConfigurationMacros, IncludePath))
StringRef ConfigurationMacros, StringRef IncludePath,
StringRef APINotesFile),
(Scope, Name, ConfigurationMacros, IncludePath,
APINotesFile))
DEFINE_MDNODE_GET(DIModule,
(Metadata *Scope, MDString *Name, MDString *ConfigurationMacros,
MDString *IncludePath),
(Scope, Name, ConfigurationMacros, IncludePath))
(Metadata * Scope, MDString *Name,
MDString *ConfigurationMacros, MDString *IncludePath,
MDString *APINotesFile),
(Scope, Name, ConfigurationMacros, IncludePath,
APINotesFile))
TempDIModule clone() const { return cloneImpl(); }
@ -2121,11 +2128,13 @@ public:
StringRef getName() const { return getStringOperand(1); }
StringRef getConfigurationMacros() const { return getStringOperand(2); }
StringRef getIncludePath() const { return getStringOperand(3); }
StringRef getAPINotesFile() const { return getStringOperand(4); }
Metadata *getRawScope() const { return getOperand(0); }
MDString *getRawName() const { return getOperandAs<MDString>(1); }
MDString *getRawConfigurationMacros() const { return getOperandAs<MDString>(2); }
MDString *getRawIncludePath() const { return getOperandAs<MDString>(3); }
MDString *getRawAPINotesFile() const { return getOperandAs<MDString>(4); }
static bool classof(const Metadata *MD) {
return MD->getMetadataID() == DIModuleKind;

View File

@ -4827,18 +4827,20 @@ bool LLParser::ParseDIMacroFile(MDNode *&Result, bool IsDistinct) {
/// ParseDIModule:
/// ::= !DIModule(scope: !0, name: "SomeModule", configMacros: "-DNDEBUG",
/// includePath: "/usr/include")
/// includePath: "/usr/include", apinotes: "module.apinotes")
bool LLParser::ParseDIModule(MDNode *&Result, bool IsDistinct) {
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
REQUIRED(scope, MDField, ); \
REQUIRED(name, MDStringField, ); \
OPTIONAL(configMacros, MDStringField, ); \
OPTIONAL(includePath, MDStringField, );
OPTIONAL(includePath, MDStringField, ); \
OPTIONAL(apinotes, MDStringField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS
Result = GET_OR_DISTINCT(DIModule, (Context, scope.Val, name.Val,
configMacros.Val, includePath.Val));
Result =
GET_OR_DISTINCT(DIModule, (Context, scope.Val, name.Val, configMacros.Val,
includePath.Val, apinotes.Val));
return false;
}

View File

@ -1418,14 +1418,15 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
}
case bitc::METADATA_MODULE: {
if (Record.size() < 5 || Record.size() > 6)
if (Record.size() < 5 || Record.size() > 7)
return error("Invalid record");
IsDistinct = Record[0];
MetadataList.assignValue(
GET_OR_DISTINCT(
DIModule, (Context, getMDOrNull(Record[1]), getMDString(Record[2]),
getMDString(Record[3]), getMDString(Record[4]))),
GET_OR_DISTINCT(DIModule,
(Context, getMDOrNull(Record[1]),
getMDString(Record[2]), getMDString(Record[3]),
getMDString(Record[4]), getMDString(Record[5]))),
NextMetadataNo);
NextMetadataNo++;
break;

View File

@ -1126,6 +1126,8 @@ DIE *DwarfUnit::getOrCreateModule(const DIModule *M) {
M->getConfigurationMacros());
if (!M->getIncludePath().empty())
addString(MDie, dwarf::DW_AT_LLVM_include_path, M->getIncludePath());
if (!M->getAPINotesFile().empty())
addString(MDie, dwarf::DW_AT_LLVM_apinotes, M->getAPINotesFile());
return &MDie;
}

View File

@ -2061,6 +2061,7 @@ static void writeDIModule(raw_ostream &Out, const DIModule *N,
Printer.printString("name", N->getName());
Printer.printString("configMacros", N->getConfigurationMacros());
Printer.printString("includePath", N->getIncludePath());
Printer.printString("apinotes", N->getAPINotesFile());
Out << ")";
}

View File

@ -831,9 +831,10 @@ DINamespace *DIBuilder::createNameSpace(DIScope *Scope, StringRef Name,
DIModule *DIBuilder::createModule(DIScope *Scope, StringRef Name,
StringRef ConfigurationMacros,
StringRef IncludePath) {
StringRef IncludePath,
StringRef APINotesFile) {
return DIModule::get(VMContext, getNonCompileUnitScope(Scope), Name,
ConfigurationMacros, IncludePath);
ConfigurationMacros, IncludePath, APINotesFile);
}
DILexicalBlockFile *DIBuilder::createLexicalBlockFile(DIScope *Scope,

View File

@ -791,11 +791,13 @@ LLVMMetadataRef
LLVMDIBuilderCreateModule(LLVMDIBuilderRef Builder, LLVMMetadataRef ParentScope,
const char *Name, size_t NameLen,
const char *ConfigMacros, size_t ConfigMacrosLen,
const char *IncludePath, size_t IncludePathLen) {
const char *IncludePath, size_t IncludePathLen,
const char *APINotesFile, size_t APINotesFileLen) {
return wrap(unwrap(Builder)->createModule(
unwrapDI<DIScope>(ParentScope), StringRef(Name, NameLen),
StringRef(ConfigMacros, ConfigMacrosLen),
StringRef(IncludePath, IncludePathLen)));
StringRef(IncludePath, IncludePathLen),
StringRef(APINotesFile, APINotesFileLen)));
}
LLVMMetadataRef LLVMDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder,

View File

@ -717,12 +717,13 @@ DICommonBlock *DICommonBlock::getImpl(LLVMContext &Context, Metadata *Scope,
DIModule *DIModule::getImpl(LLVMContext &Context, Metadata *Scope,
MDString *Name, MDString *ConfigurationMacros,
MDString *IncludePath, StorageType Storage,
bool ShouldCreate) {
MDString *IncludePath, MDString *APINotesFile,
StorageType Storage, bool ShouldCreate) {
assert(isCanonical(Name) && "Expected canonical MDString");
DEFINE_GETIMPL_LOOKUP(DIModule,
(Scope, Name, ConfigurationMacros, IncludePath));
Metadata *Ops[] = {Scope, Name, ConfigurationMacros, IncludePath};
DEFINE_GETIMPL_LOOKUP(
DIModule, (Scope, Name, ConfigurationMacros, IncludePath, APINotesFile));
Metadata *Ops[] = {Scope, Name, ConfigurationMacros, IncludePath,
APINotesFile};
DEFINE_GETIMPL_STORE_NO_CONSTRUCTOR_ARGS(DIModule, Ops);
}

View File

@ -819,20 +819,23 @@ template <> struct MDNodeKeyImpl<DIModule> {
MDString *Name;
MDString *ConfigurationMacros;
MDString *IncludePath;
MDString *APINotesFile;
MDNodeKeyImpl(Metadata *Scope, MDString *Name, MDString *ConfigurationMacros,
MDString *IncludePath)
MDString *IncludePath, MDString *APINotesFile)
: Scope(Scope), Name(Name), ConfigurationMacros(ConfigurationMacros),
IncludePath(IncludePath) {}
IncludePath(IncludePath), APINotesFile(APINotesFile) {}
MDNodeKeyImpl(const DIModule *N)
: Scope(N->getRawScope()), Name(N->getRawName()),
ConfigurationMacros(N->getRawConfigurationMacros()),
IncludePath(N->getRawIncludePath()) {}
IncludePath(N->getRawIncludePath()),
APINotesFile(N->getRawAPINotesFile()) {}
bool isKeyOf(const DIModule *RHS) const {
return Scope == RHS->getRawScope() && Name == RHS->getRawName() &&
ConfigurationMacros == RHS->getRawConfigurationMacros() &&
IncludePath == RHS->getRawIncludePath();
IncludePath == RHS->getRawIncludePath() &&
APINotesFile == RHS->getRawAPINotesFile();
}
unsigned getHashValue() const {

View File

@ -1,8 +1,8 @@
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
; RUN: verify-uselistorder %s
; CHECK: !named = !{!0, !1, !2, !1}
!named = !{!0, !1, !2, !3}
; CHECK: !named = !{!0, !1, !2, !1, !3}
!named = !{!0, !1, !2, !3, !4}
!0 = distinct !{}
@ -13,3 +13,6 @@
!2 = !DIModule(scope: !0, name: "Module", configMacros: "-DNDEBUG", includePath: "/usr/include")
!3 = !DIModule(scope: !0, name: "Module", configMacros: "")
; CHECK: !3 = !DIModule(scope: !0, name: "Module", configMacros: "-DNDEBUG", includePath: "/usr/include", apinotes: "/tmp/m.apinotes")
!4 = !DIModule(scope: !0, name: "Module", configMacros: "-DNDEBUG", includePath: "/usr/include", apinotes: "/tmp/m.apinotes")

View File

@ -9,6 +9,7 @@
; CHECK-NEXT: DW_AT_name {{.*}}"DebugModule"
; CHECK-NEXT: DW_AT_LLVM_config_macros {{.*}}"-DMODULES=0"
; CHECK-NEXT: DW_AT_LLVM_include_path {{.*}}"/llvm/tools/clang/test/Modules/Inputs"
; CHECK-NEXT: DW_AT_LLVM_apinotes {{.*}}"m.apinotes"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx"
@ -22,7 +23,7 @@ target triple = "x86_64-apple-macosx"
!2 = !{}
!3 = !{!4}
!4 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !5, file: !1, line: 5)
!5 = !DIModule(scope: null, name: "DebugModule", configMacros: "-DMODULES=0", includePath: "/llvm/tools/clang/test/Modules/Inputs")
!5 = !DIModule(scope: null, name: "DebugModule", configMacros: "-DMODULES=0", includePath: "/llvm/tools/clang/test/Modules/Inputs", apinotes: "m.apinotes")
!6 = !{i32 2, !"Dwarf Version", i32 4}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !{!"LLVM version 3.7.0"}

View File

@ -44,13 +44,15 @@ int llvm_test_dibuilder(void) {
LLVMDIBuilderCreateModule(DIB, CompileUnit,
"llvm-c-test", 11,
"", 0,
"/test/include/llvm-c-test.h", 27);
"/test/include/llvm-c-test.h", 27,
"", 0);
LLVMMetadataRef OtherModule =
LLVMDIBuilderCreateModule(DIB, CompileUnit,
"llvm-c-test-import", 18,
"", 0,
"/test/include/llvm-c-test-import.h", 34);
"/test/include/llvm-c-test-import.h", 34,
"", 0);
LLVMMetadataRef ImportedModule =
LLVMDIBuilderCreateImportedModuleFromModule(DIB, Module, OtherModule,
File, 42);

View File

@ -2057,19 +2057,28 @@ TEST_F(DIModuleTest, get) {
StringRef Name = "module";
StringRef ConfigMacro = "-DNDEBUG";
StringRef Includes = "-I.";
StringRef APINotes = "/tmp/m.apinotes";
auto *N = DIModule::get(Context, Scope, Name, ConfigMacro, Includes);
auto *N = DIModule::get(Context, Scope, Name, ConfigMacro, Includes, APINotes);
EXPECT_EQ(dwarf::DW_TAG_module, N->getTag());
EXPECT_EQ(Scope, N->getScope());
EXPECT_EQ(Name, N->getName());
EXPECT_EQ(ConfigMacro, N->getConfigurationMacros());
EXPECT_EQ(Includes, N->getIncludePath());
EXPECT_EQ(N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes));
EXPECT_NE(N, DIModule::get(Context, getFile(), Name, ConfigMacro, Includes));
EXPECT_NE(N, DIModule::get(Context, Scope, "other", ConfigMacro, Includes));
EXPECT_NE(N, DIModule::get(Context, Scope, Name, "other", Includes));
EXPECT_NE(N, DIModule::get(Context, Scope, Name, ConfigMacro, "other"));
EXPECT_EQ(APINotes, N->getAPINotesFile());
EXPECT_EQ(
N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes, APINotes));
EXPECT_NE(N, DIModule::get(Context, getFile(), Name, ConfigMacro, Includes,
APINotes));
EXPECT_NE(N, DIModule::get(Context, Scope, "other", ConfigMacro, Includes,
APINotes));
EXPECT_NE(N,
DIModule::get(Context, Scope, Name, "other", Includes, APINotes));
EXPECT_NE(
N, DIModule::get(Context, Scope, Name, ConfigMacro, "other", APINotes));
EXPECT_NE(
N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes, "other"));
TempDIModule Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));