Index: test/tools/gold/X86/thinlto_prefix_replace.ll =================================================================== --- /dev/null +++ test/tools/gold/X86/thinlto_prefix_replace.ll @@ -0,0 +1,16 @@ +; Check that changing the output path via thinlto-prefix-replace works +; RUN: mkdir -p %T/oldpath +; RUN: opt -module-summary %s -o %T/oldpath/thinlto_prefix_replace.o +; RUN: rm -f %T/newpath/thinlto_prefix_replace.o.thinlto.bc +; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \ +; RUN: --plugin-opt=thinlto \ +; RUN: --plugin-opt=thinlto-index-only \ +; RUN: --plugin-opt=thinlto-prefix-replace="%T/oldpath/:%T/newpath/" \ +; RUN: -shared %T/oldpath/thinlto_prefix_replace.o -o %T/thinlto_prefix_replace +; RUN: ls -l %T/newpath/thinlto_prefix_replace.o.thinlto.bc | FileCheck %s +; CHECK: thinlto_prefix_replace.o.thinlto.bc + +define void @f() { +entry: + ret void +} Index: tools/gold/gold-plugin.cpp =================================================================== --- tools/gold/gold-plugin.cpp +++ tools/gold/gold-plugin.cpp @@ -189,6 +189,13 @@ // bitcode file, listing the files it imports from in plain text. This is to // support distributed build file staging. static bool thinlto_emit_imports_files = false; + // Option to control where files for a distributed backend (the individual + // index files and optional imports files) are created. + // If specified, expects a string of the form "oldprefix:newprefix", and + // instead of generating these files in the same directory path as the + // corresponding bitcode file, will use a path formed by replacing the + // bitcode file's path prefix matching oldprefix with newprefix. + static std::string thinlto_prefix_replace; // Additional options to pass into the code generator. // Note: This array will contain all plugin options which are not claimed // as plugin exclusive to pass to the code generator. @@ -224,6 +231,8 @@ thinlto_index_only = true; } else if (opt == "thinlto-emit-imports-files") { thinlto_emit_imports_files = true; + } else if (opt.startswith("thinlto-prefix-replace=")) { + thinlto_prefix_replace = opt.substr(strlen("thinlto-prefix-replace=")); } else if (opt.size() == 2 && opt[0] == 'O') { if (opt[1] < '0' || opt[1] > '3') message(LDPL_FATAL, "Optimization level must be between 0 and 3"); @@ -1199,6 +1208,36 @@ Task.cleanup(); } +/// Returns the path formed by replacing the path prefix of \p OrigModulePath +/// that matches \p OrigPrefix with \p NewPrefix. The resulting directory is +/// created if it doesn't exist. +static std::string getOutputModulePath(StringRef OrigModulePath, + std::string OrigPrefix, + std::string NewPrefix) { + if (options::thinlto_prefix_replace.empty() || + !OrigModulePath.startswith(OrigPrefix)) + return OrigModulePath; + + // First compute the new module path by stripping the old prefix and + // prepending the new prefix. + StringRef ModuleRelPath = OrigModulePath.substr(OrigPrefix.size()); + std::string NewModulePath = NewPrefix + ModuleRelPath.str(); + + // Now create the directory if it doesn't exist. + std::string NewDir = NewPrefix; + // See if ModuleRelPath (the part of the path not replaced above) contains + // a path. If so, append it to the NewPrefix to get the directory. + size_t last_dir_sep = ModuleRelPath.rfind('/'); + if (last_dir_sep != StringRef::npos) + NewDir += ModuleRelPath.slice(0, last_dir_sep).str(); + // Make sure the new directory exists, creating it if necessary. + if (std::error_code EC = llvm::sys::fs::create_directories(NewDir)) + llvm::errs() << "warning: could not create directory '" << NewDir + << "': " << EC.message() << '\n'; + + return NewModulePath; +} + /// Perform ThinLTO link, which creates the combined index file. /// Also, either launch backend threads or (under thinlto-index-only) /// emit individual index files for distributed backends and exit. @@ -1237,17 +1276,29 @@ ComputeCrossModuleImport(CombinedIndex, ModuleToDefinedGVSummaries, ImportLists, ExportLists); + // If the thinlto-prefix-replace option was specified, parse it and + // extract the old and new prefixes. + StringRef PrefixReplace = options::thinlto_prefix_replace; + assert(PrefixReplace.empty() || PrefixReplace.find(":") != StringRef::npos); + StringRef PrefixReplaceStr = PrefixReplace; + std::pair Split = PrefixReplaceStr.split(":"); + std::string OrigPrefix = Split.first.str(); + std::string NewPrefix = Split.second.str(); + // For each input bitcode file, generate an individual index that // contains summaries only for its own global values, and for any that // should be imported. for (claimed_file &F : Modules) { PluginInputFile InputFile(F.handle); std::error_code EC; - raw_fd_ostream OS((Twine(InputFile.file().name) + ".thinlto.bc").str(), - EC, sys::fs::OpenFlags::F_None); + + std::string NewModulePath = + getOutputModulePath(InputFile.file().name, OrigPrefix, NewPrefix); + raw_fd_ostream OS((Twine(NewModulePath) + ".thinlto.bc").str(), EC, + sys::fs::OpenFlags::F_None); if (EC) message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s", - InputFile.file().name, EC.message().c_str()); + NewModulePath.c_str(), EC.message().c_str()); // Build a map of module to the GUIDs and summary objects that should // be written to its index. // Use a std::map instead of StringMap to get stable order for @@ -1268,12 +1319,11 @@ } } if (options::thinlto_emit_imports_files) { - raw_fd_ostream ImportsOS( - (Twine(InputFile.file().name) + ".imports").str(), EC, - sys::fs::OpenFlags::F_None); + raw_fd_ostream ImportsOS((Twine(NewModulePath) + ".imports").str(), EC, + sys::fs::OpenFlags::F_None); if (EC) message(LDPL_FATAL, "Unable to open %s.imports", - InputFile.file().name, EC.message().c_str()); + NewModulePath.c_str(), EC.message().c_str()); if (ModuleImports != ImportLists.end()) for (auto &ILI : ModuleImports->second) ImportsOS << ILI.first() << "\n";