diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 713b15b0ad..11d1544de3 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -223,6 +223,32 @@ cl::opt emitDwarfDebugInfo( "gdwarf", cl::ZeroOrMore, cl::desc("Emit DWARF debuginfo (instead of CodeView) for MSVC targets")); +// Prefix map for filenames in DWARF debuginfo +std::map debugPrefixMap; + +struct DwarfPrefixParser : public cl::parser { + explicit DwarfPrefixParser(cl::Option &O) : cl::parser(O) {} + + bool parse(cl::Option &O, llvm::StringRef /*ArgName*/, llvm::StringRef Arg, + std::string & /*Val*/) { + auto [from, to] = Arg.split('='); + if (from.empty() || to.empty()) { + return O.error("invalid debug prefix map: " + Arg); + } + auto fromStr = std::string(from); + if (debugPrefixMap.find(fromStr) != debugPrefixMap.end()) { + return O.error("debug prefix map already contains: " + fromStr); + } + debugPrefixMap[fromStr] = std::string(to); + return false; + } +}; + +static cl::opt fdebugPrefixMap( + "fdebug-prefix-map", cl::ZeroOrMore, + cl::desc("Prefix map for filenames in DWARF debuginfo"), + cl::value_desc("=")); + cl::opt noAsm("noasm", cl::desc("Disallow use of inline assembler"), cl::ZeroOrMore); diff --git a/driver/cl_options.h b/driver/cl_options.h index 01596ccc8e..6fe5a5b80f 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -21,6 +21,7 @@ #include "llvm/Support/CodeGen.h" #include "llvm/Support/CommandLine.h" #include +#include #include namespace llvm { @@ -46,6 +47,7 @@ extern cl::list runargs; extern cl::opt invokedByLDMD; extern cl::opt compileOnly; extern cl::opt emitDwarfDebugInfo; +extern std::map debugPrefixMap; // Prefix map for filenames in DWARF debuginfo extern cl::opt noAsm; extern cl::opt dontWriteObj; extern cl::opt objectFile; diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 61ed333f7e..5c2b095aa7 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -218,27 +218,70 @@ void DIBuilder::SetValue(Loc loc, llvm::Value *value, IR->scopebb()); } +std::string DIBuilder::remapDIPath(llvm::StringRef path) { +#if LDC_LLVM_VER >= 1800 +#define startswith starts_with +#endif + + for (const auto &[from, to] : opts::debugPrefixMap) { + if (path.startswith(from)) { + return to + path.substr(from.size()).str(); + } + } + return std::string(path); + +#if LDC_LLVM_VER >= 1800 +#undef startswith +#endif +} + DIFile DIBuilder::CreateFile(const char *filename) { if (!filename) filename = IR->dmodule->srcfile.toChars(); - // clang appears to use the curent working dir as 'directory' for relative - // source paths, and the root path for absolute ones: - // clang -g -emit-llvm -S ..\blub.c => - // !DIFile(filename: "..\\blub.c", directory: "C:\\LDC\\ninja-ldc", ...) - // !DIFile(filename: "Program - // Files\\LLVM\\lib\\clang\\11.0.1\\include\\stddef.h", directory: "C:\\", - // ...) - - if (llvm::sys::path::is_absolute(filename)) { - return DBuilder.createFile(llvm::sys::path::relative_path(filename), - llvm::sys::path::root_path(filename)); - } + // Mimic clang's behavior as much as possible, including fdebug-prefix-map + // remap behavior. See LLVM source clang/lib/CodeGen/CGDebugInfo.cpp + + auto remappedFile = remapDIPath(filename); llvm::SmallString<128> cwd; llvm::sys::fs::current_path(cwd); + auto currentDir = remapDIPath(cwd); + + llvm::StringRef debuginfoDir; + llvm::StringRef debuginfoFile; + + llvm::SmallString<128> DirBuf; + llvm::SmallString<128> FileBuf; + if (llvm::sys::path::is_absolute(remappedFile)) { + // Strip the common prefix (if it is more than just "/" or "C:\") from + // current directory and filename for a more space-efficient encoding. + auto FileIt = llvm::sys::path::begin(remappedFile); + auto FileE = llvm::sys::path::end(remappedFile); + auto CurDirIt = llvm::sys::path::begin(currentDir); + auto CurDirE = llvm::sys::path::end(currentDir); + for (; CurDirIt != CurDirE && *CurDirIt == *FileIt; ++CurDirIt, ++FileIt) + llvm::sys::path::append(DirBuf, *CurDirIt); + if (llvm::sys::path::root_path(DirBuf) == DirBuf) { + // Don't strip the common prefix if it is only the root ("/" or "C:\") + // since that would make diagnostic locations confusing. + debuginfoDir = {}; + debuginfoFile = remappedFile; + } else { + for (; FileIt != FileE; ++FileIt) + llvm::sys::path::append(FileBuf, *FileIt); + debuginfoDir = DirBuf; + debuginfoFile = FileBuf; + } + } else { + if (!llvm::sys::path::is_absolute(filename)) + debuginfoDir = currentDir; + debuginfoFile = remappedFile; + } - return DBuilder.createFile(filename, cwd); + // TODO: If debug file creation turns out to cost a lot of time, implement a + // caching mechanism like clang does. + return DBuilder.createFile(debuginfoFile, debuginfoDir); } DIFile DIBuilder::CreateFile(Loc loc) { diff --git a/gen/dibuilder.h b/gen/dibuilder.h index 01ee7d0477..2d506e38db 100644 --- a/gen/dibuilder.h +++ b/gen/dibuilder.h @@ -163,6 +163,7 @@ class DIBuilder { llvm::SmallVector &elems); void AddStaticMembers(AggregateDeclaration *sd, ldc::DIFile file, llvm::SmallVector &elems); + std::string remapDIPath(llvm::StringRef path); DIFile CreateFile(const char *filename = nullptr); DIFile CreateFile(Loc loc); DIFile CreateFile(Dsymbol *decl); diff --git a/tests/debuginfo/fdebug_prefix_map.d b/tests/debuginfo/fdebug_prefix_map.d new file mode 100644 index 0000000000..1b7ffc3e8f --- /dev/null +++ b/tests/debuginfo/fdebug_prefix_map.d @@ -0,0 +1,9 @@ +// RUN: %ldc -g -fdebug-prefix-map=%S=/blablabla -output-ll -of=%t.ll %s +// RUN: FileCheck %s < %t.ll + +// Check that the substitution took place. Could be as `filename: "/blablabla...` or as `directory: "/blablabla...` depending on the invoke path for lit testsuite. +// CHECK: !DIFile({{.*}}: "/blablabla + +void foo() +{ +}