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/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,71 @@ 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) + ".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: 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;