diff --git a/lld/test/wasm/reproduce.ll b/lld/test/wasm/reproduce.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/reproduce.ll @@ -0,0 +1,23 @@ +; REQUIRES: shell +; RUN: rm -rf %t.dir +; RUN: mkdir -p %t.dir +; RUN: llc -filetype=obj %s -o %t.dir/foo.o +; RUN: wasm-ld --reproduce=%t.dir/repro.tar -o out.wasm %t.dir/foo.o + +; RUN: cd %t.dir +; RUN: tar xf repro.tar +; RUN: ls repro/%t.dir/foo.o + +; RUN: FileCheck %s < repro/response.txt + +; CHECK: -o out.wasm +; CHECK: {{.*}}/foo.o + +; RUN: FileCheck %s --check-prefix=VERSION < repro/version.txt +; VERSION: LLD + +target triple = "wasm32-unknown-unknown" + +define void @_start() { + ret void +} diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -16,6 +16,7 @@ #include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Reproduce.h" #include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "lld/Common/Version.h" @@ -26,6 +27,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #define DEBUG_TYPE "lld" @@ -504,6 +506,34 @@ "__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN); } +// Reconstructs command line arguments so that so that you can re-run +// the same command with the same inputs. This is for --reproduce. +static std::string createResponseFile(const opt::InputArgList &Args) { + SmallString<0> Data; + raw_svector_ostream OS(Data); + + // Copy the command line to the output while rewriting paths. + for (auto *Arg : Args) { + switch (Arg->getOption().getUnaliasedOption().getID()) { + case OPT_reproduce: + break; + case OPT_INPUT: + OS << quote(relativeToRoot(Arg->getValue())) << "\n"; + break; + case OPT_o: + // If -o path contains directories, "lld @response.txt" will likely + // fail because the archive we are creating doesn't contain empty + // directories for the output path (-o doesn't create directories). + // Strip directories to prevent the issue. + OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n"; + break; + default: + OS << toString(*Arg) << "\n"; + } + } + return Data.str(); +} + void LinkerDriver::link(ArrayRef ArgsArr) { WasmOptTable Parser; opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); @@ -522,6 +552,20 @@ return; } + // Handle --reprodue + if (auto *Arg = Args.getLastArg(OPT_reproduce)) { + StringRef Path = Arg->getValue(); + Expected> ErrOrWriter = + TarWriter::create(Path, path::stem(Path)); + if (ErrOrWriter) { + Tar = std::move(*ErrOrWriter); + Tar->append("response.txt", createResponseFile(Args)); + Tar->append("version.txt", getLLDVersion() + "\n"); + } else { + error("--reproduce: " + toString(ErrOrWriter.takeError())); + } + } + // Parse and evaluate -mllvm options. std::vector V; V.push_back("wasm-ld (LLVM option parsing)"); diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -19,6 +19,10 @@ #include "llvm/Support/MemoryBuffer.h" #include +namespace llvm { +class TarWriter; +} + namespace lld { namespace wasm { @@ -29,6 +33,10 @@ class InputEvent; class InputSection; +// If -reproduce option is given, all input files are written +// to this tar archive. +extern std::unique_ptr Tar; + class InputFile { public: enum Kind { diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -14,8 +14,10 @@ #include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Reproduce.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Wasm.h" +#include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" #define DEBUG_TYPE "lld" @@ -27,6 +29,8 @@ using namespace llvm::object; using namespace llvm::wasm; +std::unique_ptr lld::wasm::Tar; + Optional lld::wasm::readFile(StringRef Path) { log("Loading: " + Path); @@ -39,6 +43,8 @@ MemoryBufferRef MBRef = MB->getMemBufferRef(); make>(std::move(MB)); // take MB ownership + if (Tar) + Tar->append(relativeToRoot(Path), MBRef.getBuffer()); return MBRef; } @@ -55,7 +61,6 @@ if (Magic == file_magic::bitcode) return make(MB, ArchiveName); - fatal("unknown file type: " + MB.getBufferIdentifier()); } diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -85,6 +85,8 @@ def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; +defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">; + def shared: F<"shared">, HelpText<"Build a shared object">; def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;