Index: include/llvm/LTO/Config.h =================================================================== --- include/llvm/LTO/Config.h +++ include/llvm/LTO/Config.h @@ -73,6 +73,12 @@ /// Sample PGO profile path. std::string SampleProfile; + /// The directory to store .dwo files. + std::string DwoDir; + + /// The objcopy binary used to extract dwo files. + std::string Objcopy; + /// Optimization remarks file path. std::string RemarksFilename = ""; Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -136,6 +136,8 @@ AddString(Conf.AAPipeline); AddString(Conf.OverrideTriple); AddString(Conf.DefaultTriple); + if (!Conf.DwoDir.empty()) + AddString(Conf.DwoDir); // Include the hash for the current module auto ModHash = Index.getModuleHash(ModuleID); Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -30,6 +30,10 @@ #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Target/TargetMachine.h" @@ -279,11 +283,74 @@ return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod); } +void codegenWithSplitDwarf(Config &Conf, TargetMachine *TM, + AddStreamFn AddStream, unsigned Task, Module &Mod) { + SmallString<128> TempFile; + int FD = -1; + if (auto EC = + sys::fs::createTemporaryFile("lto-llvm-fission", "o", FD, TempFile)) + report_fatal_error("Could not create temporary file " + + TempFile.str() + ": " + EC.message()); + llvm::raw_fd_ostream OS(FD, true); + SmallString<1024> DwarfFile(Conf.DwoDir); + std::string DwoName = sys::path::filename(Mod.getModuleIdentifier()).str() + + "-" + std::to_string(Task) + "-"; + size_t index = TempFile.str().rfind("lto-llvm-fission"); + StringRef TempID = TempFile.str().substr(index + 17, 6); + DwoName += TempID.str() + ".dwo"; + sys::path::append(DwarfFile, DwoName); + TM->Options.MCOptions.SplitDwarfFile = DwarfFile.str().str(); + + legacy::PassManager CodeGenPasses; + if (TM->addPassesToEmitFile(CodeGenPasses, OS, Conf.CGFileType)) + report_fatal_error("Failed to setup codegen"); + CodeGenPasses.run(Mod); + + if (auto EC = llvm::sys::fs::create_directories(Conf.DwoDir)) + report_fatal_error("Failed to create directory " + + Conf.DwoDir + ": " + EC.message()); + + SmallVector ExtractArgs, StripArgs; + ExtractArgs.push_back(Conf.Objcopy.c_str()); + ExtractArgs.push_back("--extract-dwo"); + ExtractArgs.push_back(TempFile.c_str()); + ExtractArgs.push_back(TM->Options.MCOptions.SplitDwarfFile.c_str()); + ExtractArgs.push_back(nullptr); + StripArgs.push_back(Conf.Objcopy.c_str()); + StripArgs.push_back("--strip-dwo"); + StripArgs.push_back(TempFile.c_str()); + StripArgs.push_back(nullptr); + + if (auto Ret = sys::ExecuteAndWait(Conf.Objcopy, ExtractArgs.data())) { + report_fatal_error("Failed to extract dwo from " + TempFile.str() + + ". Exit code " + std::to_string(Ret)); + } + if (auto Ret = sys::ExecuteAndWait(Conf.Objcopy, StripArgs.data())) { + report_fatal_error("Failed to strip dwo from " + TempFile.str() + + ". Exit code " + std::to_string(Ret)); + } + + auto Stream = AddStream(Task); + auto Buffer = MemoryBuffer::getFile(TempFile); + if (auto EC = Buffer.getError()) + report_fatal_error("Failed to load file " + + TempFile.str() + ": " + EC.message()); + *Stream->OS << Buffer.get()->getBuffer(); + if (auto EC = sys::fs::remove(TempFile)) + report_fatal_error("Failed to delete file " + + TempFile.str() + ": " + EC.message()); +} + void codegen(Config &Conf, TargetMachine *TM, AddStreamFn AddStream, unsigned Task, Module &Mod) { if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod)) return; + if (!Conf.DwoDir.empty()) { + codegenWithSplitDwarf(Conf, TM, AddStream, Task, Mod); + return; + } + auto Stream = AddStream(Task); legacy::PassManager CodeGenPasses; if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS, Conf.CGFileType)) Index: test/tools/gold/X86/split-dwarf.ll =================================================================== --- /dev/null +++ test/tools/gold/X86/split-dwarf.ll @@ -0,0 +1,23 @@ +UNSUPPORTED: windows +; RUN: rm -rf %t && mkdir -p %t +; RUN: cd %t +; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext \ +; RUN: -m elf_x86_64 \ +; RUN: --plugin-opt=thinlto \ +; RUN: --plugin-opt=objcopy=$(which objcopy) \ +; RUN: --plugin-opt=dwo_dir=./dwo_dir \ +; RUN: %p/Inputs/split-dwarf.bc -shared -o split-dwarf + + +; split-dwarf.bc was created from "int main() {return 0;}" with clang 7.0 and +; -g, so it contains debug info. + +; RUN: readelf --debug-dump=info split-dwarf | FileCheck %s +; CHECK: DW_AT_GNU_dwo_name{{.*}}dwo_dir/split-dwarf.bc{{.*}} +; CHECK-NOT: Abbrev Number{{.*}}DW_TAG_subprogram +; +; RUN: readelf --debug-dump=info ./dwo_dir/split-dwarf.bc* | FileCheck --check-prefix DWOFILE %s +; DWOFILE: DW_AT_GNU_dwo_name{{.*}}dwo_dir/split-dwarf.bc{{.*}} +; DWOFILE: DW_AT_name{{.*}}split-dwarf.c +; DWOFILE: Abbrev Number{{.*}}DW_TAG_subprogram + Index: tools/gold/gold-plugin.cpp =================================================================== --- tools/gold/gold-plugin.cpp +++ tools/gold/gold-plugin.cpp @@ -185,6 +185,10 @@ static std::string sample_profile; // New pass manager static bool new_pass_manager = false; + // Objcopy to debug fission. + static std::string objcopy; + // Directory to store the .dwo files. + static std::string dwo_dir; static void process_plugin_option(const char *opt_) { @@ -243,10 +247,14 @@ } else if (opt == "disable-verify") { DisableVerify = true; } else if (opt.startswith("sample-profile=")) { - sample_profile= opt.substr(strlen("sample-profile=")); + sample_profile = opt.substr(strlen("sample-profile=")); } else if (opt == "new-pass-manager") { new_pass_manager = true; - } else { + } else if (opt.startswith("objcopy=")) { + objcopy = opt.substr(strlen("objcopy=")); + } else if (opt.startswith("dwo_dir=")) { + dwo_dir = opt.substr(strlen("dwo_dir=")); + }else { // Save this option to pass to the code generator. // ParseCommandLineOptions() expects argv[0] to be program name. Lazily // add that. @@ -803,6 +811,12 @@ if (!options::sample_profile.empty()) Conf.SampleProfile = options::sample_profile; + if (!options::dwo_dir.empty()) + Conf.DwoDir = options::dwo_dir; + + if (!options::objcopy.empty()) + Conf.Objcopy = options::objcopy; + // Use new pass manager if set in driver Conf.UseNewPM = options::new_pass_manager;