[llvm] 65001f5 - [LTO][ELF] Add selective --save-temps= option
Jin Xin Ng via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 6 10:07:05 PDT 2022
Author: Jin Xin Ng
Date: 2022-07-06T10:06:18-07:00
New Revision: 65001f5777db90d6acd126e3c74773038c3a28e3
URL: https://github.com/llvm/llvm-project/commit/65001f5777db90d6acd126e3c74773038c3a28e3
DIFF: https://github.com/llvm/llvm-project/commit/65001f5777db90d6acd126e3c74773038c3a28e3.diff
LOG: [LTO][ELF] Add selective --save-temps= option
Allows specific “temps” to be saved, instead of the current all-or-nothing nature of --save-temps. Multiple of these “temps” can be saved by specifying the argument multiple times.
Differential Revision: https://reviews.llvm.org/D127778
Added:
lld/test/ELF/lto/save-temps-eq.ll
llvm/test/ThinLTO/X86/selective-save-temps.ll
Modified:
lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/LTO.cpp
lld/ELF/Options.td
llvm/include/llvm/LTO/Config.h
llvm/lib/LTO/LTOBackend.cpp
llvm/tools/llvm-lto2/llvm-lto2.cpp
Removed:
################################################################################
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index c593880d5cd31..39723f0927849 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -11,6 +11,7 @@
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
@@ -215,7 +216,7 @@ struct Configuration {
bool relocatable;
bool relrGlibc = false;
bool relrPackDynRelocs = false;
- bool saveTemps;
+ llvm::DenseSet<llvm::StringRef> saveTempsArgs;
std::vector<std::pair<llvm::GlobPattern, uint32_t>> shuffleSections;
bool singleRoRx;
bool shared;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7500b68a9beff..659f20967282a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -500,6 +500,10 @@ static void checkZOptions(opt::InputArgList &args) {
warn("unknown -z value: " + StringRef(arg->getValue()));
}
+constexpr const char *saveTempsValues[] = {
+ "resolution", "preopt", "promote", "internalize", "import",
+ "opt", "precodegen", "prelink", "combinedindex"};
+
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ELFOptTable parser;
opt::InputArgList args = parser.parse(argsArr.slice(1));
@@ -1151,7 +1155,21 @@ static void readConfigs(opt::InputArgList &args) {
config->relax = args.hasFlag(OPT_relax, OPT_no_relax, true);
config->rpath = getRpath(args);
config->relocatable = args.hasArg(OPT_relocatable);
- config->saveTemps = args.hasArg(OPT_save_temps);
+
+ if (args.hasArg(OPT_save_temps)) {
+ // --save-temps implies saving all temps.
+ for (const char *s : saveTempsValues)
+ config->saveTempsArgs.insert(s);
+ } else {
+ for (auto *arg : args.filtered(OPT_save_temps_eq)) {
+ StringRef s = arg->getValue();
+ if (llvm::is_contained(saveTempsValues, s))
+ config->saveTempsArgs.insert(s);
+ else
+ error("unknown --save-temps value: " + s);
+ }
+ }
+
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
index e44ef0d3c2c86..8c5001af3a91d 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -178,9 +178,10 @@ static lto::Config createConfig() {
if (config->ltoEmitAsm)
c.CGFileType = CGFT_AssemblyFile;
- if (config->saveTemps)
+ if (!config->saveTempsArgs.empty())
checkError(c.addSaveTemps(config->outputFile.str() + ".",
- /*UseInputModulePath*/ true));
+ /*UseInputModulePath*/ true,
+ config->saveTempsArgs));
return c;
}
@@ -365,7 +366,7 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
saveBuffer(buf[i], config->ltoObjPath + Twine(i));
}
- if (config->saveTemps) {
+ if (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
index 1d9fbcbcee3ce..c98d21717de00 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -585,6 +585,8 @@ def opt_remarks_with_hotness: FF<"opt-remarks-with-hotness">,
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,promote,internalize,import,opt,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
index 0000000000000..ed9c3970347f3
--- /dev/null
+++ b/lld/test/ELF/lto/save-temps-eq.ll
@@ -0,0 +1,124 @@
+;; This test is similar to llvm/test/ThinLTO/X86/selective-save-temps.ll
+
+; REQUIRES: x86
+; UNSUPPORTED: system-windows
+;; Unsupported on Windows due to
diff iculty with escaping "opt" across platforms.
+;; lit substitutes 'opt' with /path/to/opt.
+
+; RUN: rm -rf %t && mkdir %t && cd %t
+; RUN: mkdir all all2 all3 build subset subset2 && cd build
+
+; RUN: opt -thinlto-bc -o main.o %s
+; RUN: opt -thinlto-bc -o thin1.o %S/Inputs/thinlto.ll
+
+;; Create the .all dir with save-temps saving everything, this will be used to compare
+;; with the output from individualized save-temps later
+; RUN: ld.lld main.o thin1.o --save-temps -o %t/all/a.out
+; RUN: mv *.o.* %t/all
+;; Sanity check that everything got moved
+; RUN: ls | count 2
+
+;; Check precedence if both --save-temps and --save-temps= are present
+; RUN: ld.lld main.o thin1.o --save-temps=preopt --save-temps --save-temps=\opt -o %t/all2/a.out
+; RUN: cmp %t/all2/a.out %t/all/a.out
+; RUN: mv *.o.* %t/all2
+; RUN: ls | count 2
+; RUN:
diff -r %t/all %t/all2
+
+;; The next 9 blocks follow this structure:
+;; for each option of save-temps=
+;; Run linker and generate files
+;; Make sure a.out exists and is correct (by
diff -ing)
+;; this is the only file that should recur between runs
+;; (Also, for some stages, copy the generated files to %t/subset2 to check composability later)
+;; Move files that were expected to be generated to %t/all3
+;; Make sure there's no unexpected extra files
+;; After that, we'll
diff %t/all and %t/all3 to make sure all contents are identical
+
+;; Check preopt
+; RUN: ld.lld main.o thin1.o --save-temps=preopt
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: cp *.0.preopt.* %t/subset2
+; RUN: mv *.0.preopt.* %t/all3
+; RUN: ls | count 2
+
+;; Check promote
+; RUN: ld.lld main.o thin1.o --save-temps=promote
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: mv *.1.promote* %t/all3
+; RUN: ls | count 2
+
+;; Check internalize
+; RUN: ld.lld main.o thin1.o --save-temps=internalize
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: mv *.2.internalize* %t/all3
+; RUN: ls | count 2
+
+;; Check import
+; RUN: ld.lld main.o thin1.o --save-temps=import
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: mv *.3.import* %t/all3
+; RUN: ls | count 2
+
+;; Check opt
+; RUN: ld.lld main.o thin1.o --save-temps=\opt
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: cp *.4.opt* %t/subset2
+; RUN: mv *.4.opt* %t/all3
+; RUN: ls | count 2
+
+;; Check precodegen
+; RUN: ld.lld main.o thin1.o --save-temps=precodegen
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: mv *.5.precodegen* %t/all3
+; RUN: ls | count 2
+
+;; Check combinedindex
+; RUN: ld.lld main.o thin1.o --save-temps=combinedindex
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: mv *.index.bc %t/all3
+; RUN: mv *.index.dot %t/all3
+; RUN: ls | count 2
+
+;; Check prelink
+; RUN: ld.lld main.o thin1.o --save-temps=prelink
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: cp *.lto.o %t/subset2
+; RUN: mv *.lto.o %t/all3
+; RUN: ls | count 2
+
+;; Check resolution
+; RUN: ld.lld main.o thin1.o --save-temps=resolution
+;; %t/all3 needs at least 1 copy of a.out, move it over now since its the last block
+; RUN: mv a.out %t/all3
+; RUN: mv *.resolution.txt %t/all3
+; RUN: ls | count 2
+
+;; If no files were left out from individual stages, the .all3 dir should be identical to .all
+; RUN:
diff -r %t/all %t/all3
+
+;; Check multi-stage composability
+;; Similar to the above, but do it with a subset instead.
+;; .all -> .subset, .all3 -> .subset2
+; RUN: ld.lld main.o thin1.o --save-temps=preopt --save-temps=prelink --save-temps=\opt
+; RUN: cmp %t/all/a.out a.out && rm -f a.out
+; RUN: mv *.0.preopt.* %t/subset
+; RUN: mv *.4.opt* %t/subset
+; RUN: mv *.lto.o %t/subset
+; RUN: ls | count 2
+; RUN:
diff -r %t/subset2 %t/subset
+
+;; Check error message
+; RUN: not ld.lld --save-temps=prelink --save-temps=\opt --save-temps=notastage 2>&1 \
+; RUN: | FileCheck %s
+; CHECK: unknown --save-temps value: notastage
+
+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-unknown-linux-gnu"
+
+declare void @g()
+
+define i32 @_start() {
+ call void @g()
+ ret i32 0
+}
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index 54bb82d84d96e..b2ed8e60bd779 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -267,8 +267,12 @@ struct Config {
/// the given output file name, and (2) creates a resolution file whose name
/// is prefixed by the given output file name and sets ResolutionFile to its
/// file handle.
+ ///
+ /// SaveTempsArgs can be specified to select which temps to save.
+ /// If SaveTempsArgs is not provided, all temps are saved.
Error addSaveTemps(std::string OutputFileName,
- bool UseInputModulePath = false);
+ bool UseInputModulePath = false,
+ const DenseSet<StringRef> &SaveTempsArgs = {});
};
struct LTOLLVMDiagnosticHandler : public DiagnosticHandler {
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 5d50e92ae3773..e248e58e4e4e0 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -81,17 +81,19 @@ extern cl::opt<bool> NoPGOWarnMismatch;
exit(1);
}
-Error Config::addSaveTemps(std::string OutputFileName,
- bool UseInputModulePath) {
+Error Config::addSaveTemps(std::string OutputFileName, bool UseInputModulePath,
+ const DenseSet<StringRef> &SaveTempsArgs) {
ShouldDiscardValueNames = false;
std::error_code EC;
- ResolutionFile =
- std::make_unique<raw_fd_ostream>(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<raw_fd_ostream>(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 +127,7 @@ Error Config::addSaveTemps(std::string OutputFileName,
};
};
- 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 SaveCombinedIndex =
[=](const ModuleSummaryIndex &Index,
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
std::string Path = OutputFileName + "index.bc";
@@ -152,6 +147,31 @@ Error Config::addSaveTemps(std::string OutputFileName,
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 = SaveCombinedIndex;
+ } else {
+ if (SaveTempsArgs.contains("preopt"))
+ setHook("0.preopt", PreOptModuleHook);
+ if (SaveTempsArgs.contains("promote"))
+ setHook("1.promote", PostPromoteModuleHook);
+ if (SaveTempsArgs.contains("internalize"))
+ setHook("2.internalize", PostInternalizeModuleHook);
+ if (SaveTempsArgs.contains("import"))
+ setHook("3.import", PostImportModuleHook);
+ if (SaveTempsArgs.contains("opt"))
+ setHook("4.opt", PostOptModuleHook);
+ if (SaveTempsArgs.contains("precodegen"))
+ setHook("5.precodegen", PreCodeGenModuleHook);
+ if (SaveTempsArgs.contains("combinedindex"))
+ CombinedIndexHook = SaveCombinedIndex;
+ }
+
return Error::success();
}
diff --git a/llvm/test/ThinLTO/X86/selective-save-temps.ll b/llvm/test/ThinLTO/X86/selective-save-temps.ll
new file mode 100644
index 0000000000000..d350a82132565
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/selective-save-temps.ll
@@ -0,0 +1,160 @@
+; UNSUPPORTED: system-windows
+;; Unsupported on Windows due to
diff iculty with escaping "opt" across platforms.
+;; lit substitutes 'opt' with /path/to/opt.
+
+; RUN: rm -rf %t && mkdir %t && cd %t
+
+;; Copy IR from import-constant.ll since it generates all the temps
+; RUN: opt -thinlto-bc %s -o 1.bc
+; RUN: opt -thinlto-bc %p/Inputs/import-constant.ll -o 2.bc
+
+;; Create the .all dir with save-temps saving everything, this will be used to compare
+;; with the output from individualized save-temps later
+; RUN: mkdir all all2 build subset subset2
+; RUN: llvm-lto2 run 1.bc 2.bc -o all/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -save-temps
+
+;; The next 8 blocks follow this structure:
+;; for each option of save-temps=
+;; Run lto and generate files
+;; Make sure a.out exists and is correct (by
diff -ing)
+;; this is the only file that should recur between runs
+;; (Also, for some stages, copy the generated files to subset2 to check composability later)
+;; Move files that were expected to be generated to all2
+;; Make sure there's no unexpected extra files
+;; After that, we'll
diff all and all2 to make sure all contents are identical
+
+;; Check preopt
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=preopt
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: cp build/*.0.preopt.* subset2
+; RUN: mv build/*.0.preopt.* all2
+; RUN: ls build | count 0
+
+;; Check promote
+; RUN: rm -f all2/*.1.promote*
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=promote
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: mv build/*.1.promote* all2
+; RUN: ls build | count 0
+
+;; Check internalize
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=internalize
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: mv build/*.2.internalize* all2
+; RUN: ls build | count 0
+
+;; Check import
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=import
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: mv build/*.3.import* all2
+; RUN: ls build | count 0
+
+;; Check opt
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=\opt
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: cp build/*.4.opt* subset2
+; RUN: mv build/*.4.opt* all2
+; RUN: ls build | count 0
+
+;; Check precodegen
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=precodegen
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: mv build/*.5.precodegen* all2
+; RUN: ls build | count 0
+
+;; Check combinedindex
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=combinedindex
+; RUN: cmp all/a.out.1 build/a.out.1 && rm -f build/a.out.1
+; RUN: cmp all/a.out.2 build/a.out.2 && rm -f build/a.out.2
+; RUN: cp build/*.index.bc subset2
+; RUN: cp build/*.index.dot subset2
+; RUN: mv build/*.index.bc all2
+; RUN: mv build/*.index.dot all2
+; RUN: ls build | count 0
+
+;; Check resolution
+; RUN: llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=resolution
+;; all2 needs at least 1 copy of a.out, move it over now since its the last block
+; RUN: mv build/a.out.1 build/a.out.2 all2
+; RUN: mv build/*.resolution.txt all2
+; RUN: ls build | count 0
+
+;; If no files were left out from individual stages, the .all2 dir should be identical to .all
+; RUN:
diff -r all all2
+
+;; Check multi-stage composability
+;; Similar to the above, but do it with a subset instead.
+;; .all -> .subset, .all2 -> .subset2
+; RUN: llvm-lto2 run 1.bc 2.bc -o subset/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=preopt,combinedindex,\opt
+; RUN: cmp all/a.out.1 subset/a.out.1 && rm -f subset/a.out.1
+; RUN: cmp all/a.out.2 subset/a.out.2 && rm -f subset/a.out.2
+; RUN:
diff -r subset subset2
+
+;; Check error messages
+; RUN: not llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=prelink 2>&1 \
+; RUN: | FileCheck %s --check-prefix=ERR1
+; ERR1: invalid -select-save-temps argument: prelink
+
+; RUN: not llvm-lto2 run 1.bc 2.bc -o build/a.out \
+; RUN: -import-constants-with-refs -r=1.bc,main,plx -r=1.bc,_Z6getObjv,l \
+; RUN: -r=2.bc,_Z6getObjv,pl -r=2.bc,val,pl -r=2.bc,outer,pl \
+; RUN: -select-save-temps=preopt -save-temps 2>&1 \
+; RUN: | FileCheck %s --check-prefix=ERR2
+; ERR2: -save-temps cannot be specified with -select-save-temps
+
+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-unknown-linux-gnu"
+
+%struct.S = type { i32, i32, i32* }
+
+define dso_local i32 @main() local_unnamed_addr {
+entry:
+ %call = tail call %struct.S* @_Z6getObjv()
+ %d = getelementptr inbounds %struct.S, %struct.S* %call, i64 0, i32 0
+ %0 = load i32, i32* %d, align 8
+ %v = getelementptr inbounds %struct.S, %struct.S* %call, i64 0, i32 1
+ %1 = load i32, i32* %v, align 4
+ %add = add nsw i32 %1, %0
+ ret i32 %add
+}
+
+declare dso_local %struct.S* @_Z6getObjv() local_unnamed_addr
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index f79db36d2d2d6..87fe90a5225fc 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -67,6 +67,19 @@ static cl::opt<std::string> AAPipeline("aa-pipeline",
static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));
+static cl::list<std::string> SelectSaveTemps(
+ "select-save-temps",
+ cl::value_desc("One, or multiple of: "
+ "resolution,preopt,promote,internalize,import,opt,precodegen"
+ ",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"};
+
static cl::opt<bool>
ThinLTODistributedIndexes("thinlto-distributed-indexes",
cl::desc("Write out individual index and "
@@ -258,9 +271,22 @@ static int run(int argc, char **argv) {
Conf.DebugPassManager = DebugPassManager;
- if (SaveTemps)
- check(Conf.addSaveTemps(OutputFilename + "."),
+ if (SaveTemps && !SelectSaveTemps.empty()) {
+ llvm::errs() << "-save-temps cannot be specified with -select-save-temps\n";
+ return 1;
+ }
+ if (SaveTemps || !SelectSaveTemps.empty()) {
+ DenseSet<StringRef> SaveTempsArgs;
+ for (auto &S : SelectSaveTemps)
+ if (is_contained(SaveTempsValues, S))
+ SaveTempsArgs.insert(S);
+ else {
+ llvm::errs() << ("invalid -select-save-temps argument: " + S) << '\n';
+ return 1;
+ }
+ check(Conf.addSaveTemps(OutputFilename + ".", false, SaveTempsArgs),
"Config::addSaveTemps failed");
+ }
// Optimization remarks.
Conf.RemarksFilename = RemarksFilename;
More information about the llvm-commits
mailing list