[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