diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -535,8 +535,8 @@ } constexpr const char *saveTempsValues[] = { - "resolution", "preopt", "promote", "internalize", "import", - "opt", "precodegen", "prelink", "combinedindex"}; + "resolution", "preopt", "promote", "internalize", "import", + "opt", "precodegen", "asm", "prelink", "combinedindex"}; void LinkerDriver::linkerMain(ArrayRef argsArr) { ELFOptTable parser; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -591,7 +591,7 @@ HelpText<"The format used for serializing remarks (default: YAML)">; def save_temps: F<"save-temps">, HelpText<"Save intermediate LTO compilation results">; def save_temps_eq: JJ<"save-temps=">, HelpText<"Save select intermediate LTO compilation results">, - Values<"resolution,preopt,promote,internalize,import,opt,precodegen,prelink,combinedindex">; + Values<"resolution,preopt,promote,internalize,import,opt,precodegen,asm,prelink,combinedindex">; def lto_basic_block_sections: JJ<"lto-basic-block-sections=">, HelpText<"Enable basic block sections for LTO">; defm lto_unique_basic_block_section_names: BB<"lto-unique-basic-block-section-names", diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -239,6 +239,12 @@ /// splitting the module. ModuleHookFn PreCodeGenModuleHook; + // When this hook is non-null, and the output file type is != from + // AssemblyFile, performs codegen a second time using the output stream + // returned by this hook. + std::function(const Module &, unsigned Task)> + GetAssemblyOutputStream; + /// A combined index hook is called after all per-module indexes have been /// combined (ThinLTO-specific). It can be used to implement -save-temps for /// the combined index. diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -82,6 +82,35 @@ exit(1); } +// TODO: docs +std::unique_ptr +createTempFileOutputStream(const Module &M, std::string OutputFileName, + bool UseInputModulePath, unsigned Task, + std::string PathSuffix, std::string Extension) { + std::string PathPrefix; + // If this is the combined module (not a ThinLTO backend compile) or the + // user hasn't requested using the input module's path, emit to a file + // named from the provided OutputFileName with the Task ID appended. + if (M.getModuleIdentifier() == "ld-temp.o" || !UseInputModulePath) { + PathPrefix = OutputFileName; + if (Task != (unsigned)-1) + PathPrefix += utostr(Task) + "."; + } else + PathPrefix = M.getModuleIdentifier() + "."; + std::string Path = PathPrefix + PathSuffix; + if (!Extension.empty()) + Path += "." + Extension; + std::error_code EC; + auto OS = + std::make_unique(Path, EC, sys::fs::OpenFlags::OF_None); + // Because -save-temps is a debugging feature, we report the error + // directly and exit. + if (EC) + reportOpenError(Path, EC.message()); + + return OS; +} + Error Config::addSaveTemps(std::string OutputFileName, bool UseInputModulePath, const DenseSet &SaveTempsArgs) { ShouldDiscardValueNames = false; @@ -106,28 +135,20 @@ if (LinkerHook && !LinkerHook(Task, M)) return false; - std::string PathPrefix; - // If this is the combined module (not a ThinLTO backend compile) or the - // user hasn't requested using the input module's path, emit to a file - // named from the provided OutputFileName with the Task ID appended. - if (M.getModuleIdentifier() == "ld-temp.o" || !UseInputModulePath) { - PathPrefix = OutputFileName; - if (Task != (unsigned)-1) - PathPrefix += utostr(Task) + "."; - } else - PathPrefix = M.getModuleIdentifier() + "."; - std::string Path = PathPrefix + PathSuffix + ".bc"; - std::error_code EC; - raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::OF_None); - // Because -save-temps is a debugging feature, we report the error - // directly and exit. - if (EC) - reportOpenError(Path, EC.message()); - WriteBitcodeToFile(M, OS, /*ShouldPreserveUseListOrder=*/false); + std::unique_ptr OS = createTempFileOutputStream( + M, OutputFileName, UseInputModulePath, Task, PathSuffix, "bc"); + WriteBitcodeToFile(M, *OS, /*ShouldPreserveUseListOrder=*/false); return true; }; }; + auto setSaveAsmOutputHook = [&](std::string PathSuffix) { + GetAssemblyOutputStream = [=](const Module &M, unsigned TaskID) { + return createTempFileOutputStream(M, OutputFileName, UseInputModulePath, + TaskID, PathSuffix, "s"); + }; + }; + auto SaveCombinedIndex = [=](const ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols) { @@ -155,6 +176,7 @@ setHook("3.import", PostImportModuleHook); setHook("4.opt", PostOptModuleHook); setHook("5.precodegen", PreCodeGenModuleHook); + setSaveAsmOutputHook("6.asm"); CombinedIndexHook = SaveCombinedIndex; } else { if (SaveTempsArgs.contains("preopt")) @@ -169,6 +191,8 @@ setHook("4.opt", PostOptModuleHook); if (SaveTempsArgs.contains("precodegen")) setHook("5.precodegen", PreCodeGenModuleHook); + if (SaveTempsArgs.contains("asm")) + setSaveAsmOutputHook("6.asm"); if (SaveTempsArgs.contains("combinedindex")) CombinedIndexHook = SaveCombinedIndex; } @@ -398,18 +422,35 @@ std::unique_ptr &Stream = *StreamOrErr; TM->Options.ObjectFilenameForDebug = Stream->ObjectPathName; - legacy::PassManager CodeGenPasses; - TargetLibraryInfoImpl TLII(Triple(Mod.getTargetTriple())); - CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII)); - CodeGenPasses.add( - createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex)); - if (Conf.PreCodeGenPassesHook) - Conf.PreCodeGenPassesHook(CodeGenPasses); - if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS, - DwoOut ? &DwoOut->os() : nullptr, - Conf.CGFileType)) - report_fatal_error("Failed to setup codegen"); - CodeGenPasses.run(Mod); + const auto GetPassManager = [&]() { + legacy::PassManager CodeGenPasses; + TargetLibraryInfoImpl TLII(Triple(Mod.getTargetTriple())); + CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII)); + CodeGenPasses.add( + createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex)); + if (Conf.PreCodeGenPassesHook) + Conf.PreCodeGenPassesHook(CodeGenPasses); + return CodeGenPasses; + }; + + { + legacy::PassManager CodeGenPasses = GetPassManager(); + if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS, + DwoOut ? &DwoOut->os() : nullptr, + Conf.CGFileType)) + report_fatal_error("Failed to setup codegen"); + CodeGenPasses.run(Mod); + } + + if (Conf.CGFileType != CGFT_AssemblyFile && Conf.GetAssemblyOutputStream) { + std::unique_ptr AsmOutputStream = + Conf.GetAssemblyOutputStream(Mod, Task); + legacy::PassManager CodeGenPasses = GetPassManager(); + if (TM->addPassesToEmitFile(CodeGenPasses, *AsmOutputStream, nullptr, + CGFT_AssemblyFile)) + report_fatal_error("Failed to setup codegen"); + CodeGenPasses.run(Mod); + } if (DwoOut) DwoOut->keep(); diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp --- a/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -71,14 +71,14 @@ "select-save-temps", cl::value_desc("One, or multiple of: " "resolution,preopt,promote,internalize,import,opt,precodegen" - ",combinedindex"), + ",asm,combinedindex"), cl::desc("Save selected temporary files. Cannot be specified together with " "-save-temps"), cl::CommaSeparated); constexpr const char *SaveTempsValues[] = { - "resolution", "preopt", "promote", "internalize", - "import", "opt", "precodegen", "combinedindex"}; + "resolution", "preopt", "promote", "internalize", "import", + "opt", "precodegen", "asm", "combinedindex"}; static cl::opt ThinLTODistributedIndexes("thinlto-distributed-indexes",