diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -13,6 +13,7 @@ #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" @@ -212,6 +213,7 @@ bool relrGlibc = false; bool relrPackDynRelocs = false; bool saveTemps; + llvm::SmallSet saveTempsArgs; std::vector> shuffleSections; bool singleRoRx; bool shared; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1159,6 +1159,19 @@ config->rpath = getRpath(args); config->relocatable = args.hasArg(OPT_relocatable); config->saveTemps = args.hasArg(OPT_save_temps); + + // --save-temps takes precedence over --save-temps= + if (!config->saveTemps) { + // parse --save-temps= + for (auto &s : args.getAllArgValues(OPT_save_temps_eq)) + if (s == "resolution" || s == "preopt" || s == "postpromote" || + s == "postinternalize" || s == "postimport" || s == "postopt" || + s == "precodegen" || s == "prelink" || s == "combinedindex") + config->saveTempsArgs.insert(s); + else + error("unsupported argument '" + s + "' to option '--save-temps='"); + } + config->searchPaths = args::getStrings(args, OPT_library_path); config->sectionStartMap = getSectionStartMap(args); config->shared = args.hasArg(OPT_shared); diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -178,9 +178,10 @@ if (config->ltoEmitAsm) c.CGFileType = CGFT_AssemblyFile; - if (config->saveTemps) + if (config->saveTemps || !config->saveTempsArgs.empty()) checkError(c.addSaveTemps(config->outputFile.str() + ".", - /*UseInputModulePath*/ true)); + /*UseInputModulePath*/ true, + config->saveTempsArgs)); return c; } @@ -362,7 +363,7 @@ saveBuffer(buf[i], config->ltoObjPath + Twine(i)); } - if (config->saveTemps) { + if (config->saveTemps || config->saveTempsArgs.contains("prelink")) { if (!buf[0].empty()) saveBuffer(buf[0], config->outputFile + ".lto.o"); for (unsigned i = 1; i != maxTasks; ++i) diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -583,6 +583,8 @@ def opt_remarks_format: Separate<["--"], "opt-remarks-format">, 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,postpromote,postinternalize,postimport,postopt,precodegen,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/lld/test/ELF/lto/save-temps-eq.ll b/lld/test/ELF/lto/save-temps-eq.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/save-temps-eq.ll @@ -0,0 +1,109 @@ +; REQUIRES: x86 +; RUN: rm -fr %t* && mkdir %t && cd %t + +; partially copied from thinlto-single-module.ll +; RUN: opt -thinlto-bc -o main.o %s +; RUN: opt -thinlto-bc -o thin1.o %S/Inputs/thin1.ll +; RUN: opt -thinlto-bc -o thin2.o %S/Inputs/thin2.ll + +; Create the .all dir with save-temps saving everything, this will be used to compare +; with the output from individualized save-temps, and keep track of seen files +; RUN: mkdir %t.all +; RUN: ld.lld main.o thin1.o thin2.o --save-temps +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.all + +; Check precedence if both --save-temps and --save-temps= are present +; RUN: mkdir %t.all2 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=preopt --save-temps --save-temps=postopt +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.all2 +; RUN: for i in %t.all2/*; do diff "$i" %t.all/`basename "$i"` || exit 1; done +; RUN: diff <(ls %t.all) <(ls %t.all2) + +; Check preopt +; RUN: mkdir %t.0 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=preopt +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.0 +; RUN: for i in %t.0/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check postpromote +; RUN: mkdir %t.1 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=postpromote +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.1 +; RUN: for i in %t.1/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check postinternalize +; RUN: mkdir %t.2 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=postinternalize +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.2 +; RUN: for i in %t.2/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check postimport +; RUN: mkdir %t.3 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=postimport +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.3 +; RUN: for i in %t.3/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check postopt +; RUN: mkdir %t.4 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=postopt +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.4 +; RUN: for i in %t.4/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check precodegen +; RUN: mkdir %t.5 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=precodegen +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.5 +; RUN: for i in %t.5/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check combinedindex +; RUN: mkdir %t.6 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=combinedindex +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.6 +; RUN: for i in %t.6/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check prelink +; RUN: mkdir %t.7 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=prelink +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.7 +; RUN: for i in %t.7/*; do diff "$i" %t.all/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.all/`basename "$i"`; fi; done + +; Check resolution +; RUN: mkdir %t.8 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=resolution +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.8 +; RUN: for i in %t.8/*; do diff "$i" %t.all/`basename "$i"` || exit 1 ; rm -f %t.all/`basename "$i"`; done + +; If no files were left out from indiv stages, the .all dir should be empty now +; RUN: ls %t.all | count 0 + +; Check multi-stage +; RUN: mkdir %t.9 +; RUN: ld.lld main.o thin1.o thin2.o --save-temps=preopt --save-temps=prelink --save-temps=postopt +; RUN: ls %t/a.out | count 1 +; RUN: mv `find . ! -name main\.o ! -name thin1\.o ! -name thin2\.o ! -name \.` %t.9 +; RUN: for i in %t.0/*; do diff "$i" %t.9/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.9/`basename "$i"`; fi; done +; RUN: for i in %t.4/*; do diff "$i" %t.9/`basename "$i"` || exit 1; if [[ "`basename "$i"`" != "a.out" ]]; then rm -f %t.9/`basename "$i"`; fi; done +; RUN: for i in %t.7/*; do diff "$i" %t.9/`basename "$i"` || exit 1; rm -f %t.9/`basename "$i"`; done +; RUN: ls %t.9 | count 0 + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-scei-ps4" + +declare i32 @blah(i32 %meh) +declare i32 @foo(i32 %goo) + +define i32 @_start() { + call i32 @foo(i32 0) + call i32 @blah(i32 0) + ret i32 0 +} 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 @@ -15,6 +15,7 @@ #define LLVM_LTO_CONFIG_H #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/GlobalValue.h" @@ -268,7 +269,8 @@ /// is prefixed by the given output file name and sets ResolutionFile to its /// file handle. Error addSaveTemps(std::string OutputFileName, - bool UseInputModulePath = false); + bool UseInputModulePath = false, + const llvm::SmallSet &saveTempsArgs = {}); }; struct LTOLLVMDiagnosticHandler : public DiagnosticHandler { 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 @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "llvm/LTO/LTOBackend.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/ModuleSummaryAnalysis.h" @@ -81,17 +82,20 @@ exit(1); } -Error Config::addSaveTemps(std::string OutputFileName, - bool UseInputModulePath) { +Error Config::addSaveTemps( + std::string OutputFileName, bool UseInputModulePath, + const llvm::SmallSet &saveTempsArgs) { ShouldDiscardValueNames = false; std::error_code EC; - ResolutionFile = - std::make_unique(OutputFileName + "resolution.txt", EC, - sys::fs::OpenFlags::OF_TextWithCRLF); - if (EC) { - ResolutionFile.reset(); - return errorCodeToError(EC); + if (saveTempsArgs.empty() || saveTempsArgs.contains("resolution")) { + ResolutionFile = + std::make_unique(OutputFileName + "resolution.txt", EC, + sys::fs::OpenFlags::OF_TextWithCRLF); + if (EC) { + ResolutionFile.reset(); + return errorCodeToError(EC); + } } auto setHook = [&](std::string PathSuffix, ModuleHookFn &Hook) { @@ -125,14 +129,7 @@ }; }; - setHook("0.preopt", PreOptModuleHook); - setHook("1.promote", PostPromoteModuleHook); - setHook("2.internalize", PostInternalizeModuleHook); - setHook("3.import", PostImportModuleHook); - setHook("4.opt", PostOptModuleHook); - setHook("5.precodegen", PreCodeGenModuleHook); - - CombinedIndexHook = + auto _CombinedIndexHook = [=](const ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols) { std::string Path = OutputFileName + "index.bc"; @@ -152,6 +149,31 @@ return true; }; + if (saveTempsArgs.empty()) { + setHook("0.preopt", PreOptModuleHook); + setHook("1.promote", PostPromoteModuleHook); + setHook("2.internalize", PostInternalizeModuleHook); + setHook("3.import", PostImportModuleHook); + setHook("4.opt", PostOptModuleHook); + setHook("5.precodegen", PreCodeGenModuleHook); + CombinedIndexHook = _CombinedIndexHook; + } else { + if (saveTempsArgs.contains("preopt")) + setHook("0.preopt", PreOptModuleHook); + if (saveTempsArgs.contains("postpromote")) + setHook("1.promote", PostPromoteModuleHook); + if (saveTempsArgs.contains("postinternalize")) + setHook("2.internalize", PostInternalizeModuleHook); + if (saveTempsArgs.contains("postimport")) + setHook("3.import", PostImportModuleHook); + if (saveTempsArgs.contains("postopt")) + setHook("4.opt", PostOptModuleHook); + if (saveTempsArgs.contains("precodegen")) + setHook("5.precodegen", PreCodeGenModuleHook); + if (saveTempsArgs.contains("combinedindex")) + CombinedIndexHook = _CombinedIndexHook; + } + return Error::success(); }