if (!lto::initImportList(*M, *CombinedIndex, ImportList))
return;
- auto AddStream = [&](size_t Task) {
+ auto AddStream = [&](size_t Task, const Twine &ModuleName) {
return std::make_unique<CachedFileStream>(std::move(OS),
CGOpts.ObjectFilenameForDebug);
};
// Run the LTO job to compile the bitcode.
size_t MaxTasks = LTOBackend->getMaxTasks();
SmallVector<StringRef> Files(MaxTasks);
- auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ auto AddStream =
+ [&](size_t Task,
+ const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
int FD = -1;
auto &TempFile = Files[Task];
StringRef Extension = (Triple.isNVPTX()) ? "s" : "o";
// specified, configure LTO to use it as the cache directory.
FileCache cache;
if (!config->ltoCache.empty())
- cache =
- check(localCache("ThinLTO", "Thin", config->ltoCache,
- [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
- files[task] = std::move(mb);
- }));
+ cache = check(localCache("ThinLTO", "Thin", config->ltoCache,
+ [&](size_t task, const Twine &moduleName,
+ std::unique_ptr<MemoryBuffer> mb) {
+ files[task].first = moduleName.str();
+ files[task].second = std::move(mb);
+ }));
checkError(ltoObj->run(
- [&](size_t task) {
+ [&](size_t task, const Twine &moduleName) {
+ buf[task].first = moduleName.str();
return std::make_unique<CachedFileStream>(
- std::make_unique<raw_svector_ostream>(buf[task]));
+ std::make_unique<raw_svector_ostream>(buf[task].second));
},
cache));
// distributed environment.
if (config->thinLTOIndexOnly) {
if (!config->ltoObjPath.empty())
- saveBuffer(buf[0], config->ltoObjPath);
+ saveBuffer(buf[0].second, config->ltoObjPath);
if (indexFile)
indexFile->close();
return {};
std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i) {
- // Assign unique names to LTO objects. This ensures they have unique names
- // in the PDB if one is produced. The names should look like:
- // - foo.exe.lto.obj
- // - foo.exe.lto.1.obj
- // - ...
- StringRef ltoObjName =
- saver().save(Twine(config->outputFile) + ".lto" +
- (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
-
+ StringRef bitcodeFilePath;
// Get the native object contents either from the cache or from memory. Do
// not use the cached MemoryBuffer directly, or the PDB will not be
// deterministic.
StringRef objBuf;
- if (files[i])
- objBuf = files[i]->getBuffer();
- else
- objBuf = buf[i];
+ if (files[i].second) {
+ objBuf = files[i].second->getBuffer();
+ bitcodeFilePath = files[i].first;
+ } else {
+ objBuf = buf[i].second;
+ bitcodeFilePath = buf[i].first;
+ }
if (objBuf.empty())
continue;
+ // If the input bitcode file is path/to/a.obj, then the corresponding lto
+ // object file name will soemthing like: path/to/main.exe.lto.a.obj.
+ StringRef ltoObjName;
+ if (bitcodeFilePath == "ld-temp.o") {
+ ltoObjName =
+ saver().save(Twine(config->outputFile) + ".lto" +
+ (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
+ } else {
+ StringRef directory = sys::path::parent_path(bitcodeFilePath);
+ StringRef baseName = sys::path::filename(bitcodeFilePath);
+ StringRef outputFileBaseName = sys::path::filename(config->outputFile);
+ SmallString<64> path;
+ sys::path::append(path, directory,
+ outputFileBaseName + ".lto." + baseName);
+ sys::path::remove_dots(path, true);
+ ltoObjName = saver().save(path.str());
+ }
if (config->saveTemps)
- saveBuffer(buf[i], ltoObjName);
+ saveBuffer(buf[i].second, ltoObjName);
ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
}
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
- std::vector<SmallString<0>> buf;
- std::vector<std::unique_ptr<MemoryBuffer>> files;
+ std::vector<std::pair<std::string, SmallString<0>>> buf;
+ std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> files;
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
llvm::DenseSet<StringRef> thinIndices;
};
// specified, configure LTO to use it as the cache directory.
FileCache cache;
if (!config->thinLTOCacheDir.empty())
- cache =
- check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
- [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
- files[task] = std::move(mb);
- }));
+ cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
+ [&](size_t task, const Twine &moduleName,
+ std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
+ }));
if (!ctx.bitcodeFiles.empty())
checkError(ltoObj->run(
- [&](size_t task) {
+ [&](size_t task, const Twine &moduleName) {
return std::make_unique<CachedFileStream>(
std::make_unique<raw_svector_ostream>(buf[task]));
},
// specified, configure LTO to use it as the cache directory.
FileCache cache;
if (!config->thinLTOCacheDir.empty())
- cache =
- check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
- [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
- files[task] = std::move(mb);
- }));
+ cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
+ [&](size_t task, const Twine &moduleName,
+ std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
+ }));
checkError(ltoObj->run(
- [&](size_t task) {
+ [&](size_t task, const Twine &moduleName) {
return std::make_unique<CachedFileStream>(
std::make_unique<raw_svector_ostream>(buf[task]));
},
; Test to ensure that thinlto-index-only with lto-obj-path creates
; the native object file.
-; RUN: opt -module-summary %s -o %t1.obj
-; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t2.obj
-; RUN: rm -f %t4.obj
-; RUN: lld-link -thinlto-index-only -lto-obj-path:%t4.obj -out:t3.exe \
-; RUN: -entry:main %t1.obj %t2.obj
-; RUN: llvm-readobj -h %t4.obj | FileCheck %s
-; RUN: llvm-nm %t4.obj 2>&1 | FileCheck %s -check-prefix=SYMBOLS
-; RUN: llvm-nm %t4.obj 2>&1 | count 1
+; RUN: rm -rf %t.dir/objpath && mkdir -p %t.dir/objpath
+; RUN: opt -module-summary %s -o %t.dir/objpath/t1.obj
+; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t.dir/objpath/t2.obj
+; RUN: rm -f %t.dir/objpath/t4.obj
+; RUN: lld-link -thinlto-index-only -lto-obj-path:%t.dir/objpath/t4.obj \
+; RUN: -out:%t.dir/objpath/t3.exe -entry:main %t.dir/objpath/t1.obj \
+; RUN: %t.dir/objpath/t2.obj
+; RUN: llvm-readobj -h %t.dir/objpath/t4.obj | FileCheck %s
+; RUN: llvm-nm %t.dir/objpath/t4.obj 2>&1 | FileCheck %s -check-prefix=SYMBOLS
+; RUN: llvm-nm %t.dir/objpath/t4.obj 2>&1 | count 1
;; Ensure lld emits empty combined module if specific obj-path.
-; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath
-; RUN: lld-link /out:%t.dir/objpath/a.exe -lto-obj-path:%t4.obj \
-; RUN: -entry:main %t1.obj %t2.obj -lldsavetemps
-; RUN: ls %t.dir/objpath/a.exe.lto.* | count 3
+; RUN: lld-link /out:%t.dir/objpath/a.exe -lto-obj-path:%t.dir/objpath/t4.obj \
+; RUN: -entry:main %t.dir/objpath/t1.obj %t.dir/objpath/t2.obj -lldsavetemps
+; RUN: ls %t.dir/objpath/a.exe.lto.obj
+; RUN: ls %t.dir/objpath/a.exe.lto.t1.obj
+; RUN: ls %t.dir/objpath/a.exe.lto.t2.obj
;; Ensure lld does not emit empty combined module in default.
-; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath
+; RUN: rm %t.dir/objpath/a.exe.lto.*
; RUN: lld-link /out:%t.dir/objpath/a.exe \
-; RUN: -entry:main %t1.obj %t2.obj -lldsavetemps
-; RUN: ls %t.dir/objpath/a.exe.lto.* | count 2
+; RUN: -entry:main %t.dir/objpath/t1.obj %t.dir/objpath/t2.obj -lldsavetemps
+; RUN: ls %t.dir/objpath/a.exe.lto.t1.obj
+; RUN: ls %t.dir/objpath/a.exe.lto.t2.obj
+; RUN: not ls %t.dir/objpath/a.exe.lto.obj
; CHECK: Format: COFF-x86-64
; SYMBOLS: @feat.00
; CHECK: Modules
; CHECK: ============================================================
-; CHECK: Mod 0000 | `{{.*}}main.exe.lto.1.obj`:
-; CHECK: Obj: `{{.*}}main.exe.lto.1.obj`:
-; CHECK: Mod 0001 | `{{.*}}main.exe.lto.2.obj`:
-; CHECK: Obj: `{{.*}}main.exe.lto.2.obj`:
+; CHECK: Mod 0000 | `{{.*}}main.exe.lto.main.bc`:
+; CHECK: Obj: `{{.*}}main.exe.lto.main.bc`:
+; CHECK: Mod 0001 | `{{.*}}main.exe.lto.foo.bc`:
+; CHECK: Obj: `{{.*}}main.exe.lto.foo.bc`:
; CHECK: Mod 0002 | `* Linker *`:
; RUN: opt -thinlto-bc -o %T/thinlto/main.obj %s
; RUN: opt -thinlto-bc -o %T/thinlto/foo.obj %S/Inputs/lto-dep.ll
; RUN: lld-link /lldsavetemps /out:%T/thinlto/main.exe /entry:main /subsystem:console %T/thinlto/main.obj %T/thinlto/foo.obj
-; RUN: llvm-nm %T/thinlto/main.exe.lto.1.obj | FileCheck %s
+; RUN: llvm-nm %T/thinlto/main.exe.lto.foo.obj | FileCheck %s
; Test various possible options for /opt:lldltojobs
; RUN: lld-link /lldsavetemps /out:%T/thinlto/main.exe /entry:main /subsystem:console %T/thinlto/main.obj %T/thinlto/foo.obj /opt:lldltojobs=1
-; RUN: llvm-nm %T/thinlto/main.exe.lto.1.obj | FileCheck %s
+; RUN: llvm-nm %T/thinlto/main.exe.lto.foo.obj | FileCheck %s
; RUN: lld-link /lldsavetemps /out:%T/thinlto/main.exe /entry:main /subsystem:console %T/thinlto/main.obj %T/thinlto/foo.obj /opt:lldltojobs=all
-; RUN: llvm-nm %T/thinlto/main.exe.lto.1.obj | FileCheck %s
+; RUN: llvm-nm %T/thinlto/main.exe.lto.foo.obj | FileCheck %s
; RUN: lld-link /lldsavetemps /out:%T/thinlto/main.exe /entry:main /subsystem:console %T/thinlto/main.obj %T/thinlto/foo.obj /opt:lldltojobs=100
-; RUN: llvm-nm %T/thinlto/main.exe.lto.1.obj | FileCheck %s
+; RUN: llvm-nm %T/thinlto/main.exe.lto.foo.obj | FileCheck %s
; RUN: not lld-link /lldsavetemps /out:%T/thinlto/main.exe /entry:main /subsystem:console %T/thinlto/main.obj %T/thinlto/foo.obj /opt:lldltojobs=foo 2>&1 | FileCheck %s --check-prefix=BAD-JOBS
; BAD-JOBS: error: /opt:lldltojobs: invalid job count: foo
// specified, configure LTO to use it as the cache directory.
FileCache cache;
if (!config->thinLTOCacheDir.empty())
- cache =
- check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
- [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
- files[task] = std::move(mb);
- }));
+ cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
+ [&](size_t task, const Twine &moduleName,
+ std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
+ }));
checkError(ltoObj->run(
- [&](size_t task) {
+ [&](size_t task, const Twine &moduleName) {
return std::make_unique<CachedFileStream>(
std::make_unique<raw_svector_ostream>(buf[task]));
},
// m_take_ownership member variable to indicate if we need to take
// ownership.
- auto add_buffer = [this](unsigned task, std::unique_ptr<llvm::MemoryBuffer> m) {
+ auto add_buffer = [this](unsigned task, const llvm::Twine &moduleName,
+ std::unique_ptr<llvm::MemoryBuffer> m) {
if (m_take_ownership)
m_mem_buff_up = std::move(m);
};
// turn take ownership of the member buffer that is passed to the callback and
// put it into a member variable.
llvm::Expected<llvm::AddStreamFn> add_stream_or_err =
- m_cache_callback(task, key);
+ m_cache_callback(task, key, "");
m_take_ownership = false;
// At this point we either already called the "add_buffer" lambda with
// the data or we haven't. We can tell if we got the cached data by checking
// add_buffer lambda function from the constructor which will ignore the
// data.
llvm::Expected<llvm::AddStreamFn> add_stream_or_err =
- m_cache_callback(task, key);
+ m_cache_callback(task, key, "");
// If we reach this code then we either already called the callback with
// the data or we haven't. We can tell if we had the cached data by checking
// the CacheAddStream function pointer value below.
// want to write the data.
if (add_stream) {
llvm::Expected<std::unique_ptr<llvm::CachedFileStream>> file_or_err =
- add_stream(task);
+ add_stream(task, "");
if (file_or_err) {
llvm::CachedFileStream *cfs = file_or_err->get();
cfs->OS->write((const char *)data.data(), data.size());
/// This type defines the callback to add a file that is generated on the fly.
///
/// Stream callbacks must be thread safe.
-using AddStreamFn =
- std::function<Expected<std::unique_ptr<CachedFileStream>>(unsigned Task)>;
+using AddStreamFn = std::function<Expected<std::unique_ptr<CachedFileStream>>(
+ unsigned Task, const Twine &ModuleName)>;
/// This is the type of a file cache. To request an item from the cache, pass a
/// unique string as the Key. For hits, the cached file will be added to the
/// link and this function will return AddStreamFn(). For misses, the cache will
/// return a stream callback which must be called at most once to produce
/// content for the stream. The file stream produced by the stream callback will
-/// add the file to the link after the stream is written to.
+/// add the file to the link after the stream is written to. ModuleName is the
+/// unique module identifier for the bitcode module the cache is being checked
+/// for.
///
/// Clients generally look like this:
///
-/// if (AddStreamFn AddStream = Cache(Task, Key))
+/// if (AddStreamFn AddStream = Cache(Task, Key, ModuleName))
/// ProduceContent(AddStream);
-using FileCache =
- std::function<Expected<AddStreamFn>(unsigned Task, StringRef Key)>;
+using FileCache = std::function<Expected<AddStreamFn>(
+ unsigned Task, StringRef Key, const Twine &ModuleName)>;
/// This type defines the callback to add a pre-existing file (e.g. in a cache).
///
/// Buffer callbacks must be thread safe.
-using AddBufferFn =
- std::function<void(unsigned Task, std::unique_ptr<MemoryBuffer> MB)>;
+using AddBufferFn = std::function<void(unsigned Task, const Twine &ModuleName,
+ std::unique_ptr<MemoryBuffer> MB)>;
/// Create a local file system cache which uses the given cache name, temporary
/// file prefix, cache directory and file callback. This function does not
/// messages for errors during caching. The temporary file prefix is used in the
/// temporary file naming scheme used when writing files atomically.
Expected<FileCache> localCache(
- Twine CacheNameRef, Twine TempFilePrefixRef, Twine CacheDirectoryPathRef,
- AddBufferFn AddBuffer = [](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- });
+ const Twine &CacheNameRef, const Twine &TempFilePrefixRef,
+ const Twine &CacheDirectoryPathRef,
+ AddBufferFn AddBuffer = [](size_t Task, const Twine &ModuleName,
+ std::unique_ptr<MemoryBuffer> MB) {});
} // namespace llvm
#endif
FileCache Cache = *CacheOrErr;
// We choose an arbitrary Task parameter as we do not make use of it.
unsigned Task = 0;
- Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey);
+ Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey, "");
if (!CacheAddStreamOrErr)
return CacheAddStreamOrErr.takeError();
AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
// Perform the HTTP request and if successful, write the response body to
// the cache.
- StreamedHTTPResponseHandler Handler([&]() { return CacheAddStream(Task); },
- Client);
+ StreamedHTTPResponseHandler Handler(
+ [&]() { return CacheAddStream(Task, ""); }, Client);
HTTPRequest Request(ArtifactUrl);
Request.Headers = getHeaders();
Error Err = Client.perform(Request, Handler);
computeLTOCacheKey(Key, Conf, CombinedIndex, ModuleID, ImportList,
ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs,
CfiFunctionDecls);
- Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key);
+ Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID);
if (Error Err = CacheAddStreamOrErr.takeError())
return Err;
AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
EC.message());
}
- Expected<std::unique_ptr<CachedFileStream>> StreamOrErr = AddStream(Task);
+ Expected<std::unique_ptr<CachedFileStream>> StreamOrErr =
+ AddStream(Task, Mod.getModuleIdentifier());
if (Error Err = StreamOrErr.takeError())
report_fatal_error(std::move(Err));
std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;
// make unique temp output file to put generated code
SmallString<128> Filename;
- auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ auto AddStream =
+ [&](size_t Task,
+ const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
StringRef Extension(Config.CGFileType == CGFT_AssemblyFile ? "s" : "o");
int FD;
using namespace llvm;
-Expected<FileCache> llvm::localCache(Twine CacheNameRef,
- Twine TempFilePrefixRef,
- Twine CacheDirectoryPathRef,
+Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
+ const Twine &TempFilePrefixRef,
+ const Twine &CacheDirectoryPathRef,
AddBufferFn AddBuffer) {
// Create local copies which are safely captured-by-copy in lambdas
TempFilePrefixRef.toVector(TempFilePrefix);
CacheDirectoryPathRef.toVector(CacheDirectoryPath);
- return [=](unsigned Task, StringRef Key) -> Expected<AddStreamFn> {
+ return [=](unsigned Task, StringRef Key,
+ const Twine &ModuleName) -> Expected<AddStreamFn> {
// This choice of file name allows the cache to be pruned (see pruneCache()
// in include/llvm/Support/CachePruning.h).
SmallString<64> EntryPath;
/*RequiresNullTerminator=*/false);
sys::fs::closeFile(*FDOrErr);
if (MBOrErr) {
- AddBuffer(Task, std::move(*MBOrErr));
+ AddBuffer(Task, ModuleName, std::move(*MBOrErr));
return AddStreamFn();
}
EC = MBOrErr.getError();
struct CacheStream : CachedFileStream {
AddBufferFn AddBuffer;
sys::fs::TempFile TempFile;
+ std::string ModuleName;
unsigned Task;
CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddBufferFn AddBuffer,
sys::fs::TempFile TempFile, std::string EntryPath,
- unsigned Task)
+ std::string ModuleName, unsigned Task)
: CachedFileStream(std::move(OS), std::move(EntryPath)),
AddBuffer(std::move(AddBuffer)), TempFile(std::move(TempFile)),
- Task(Task) {}
+ ModuleName(ModuleName), Task(Task) {}
~CacheStream() {
// TODO: Manually commit rather than using non-trivial destructor,
TempFile.TmpName + " to " + ObjectPathName + ": " +
toString(std::move(E)) + "\n");
- AddBuffer(Task, std::move(*MBOrErr));
+ AddBuffer(Task, ModuleName, std::move(*MBOrErr));
}
};
- return [=](size_t Task) -> Expected<std::unique_ptr<CachedFileStream>> {
+ return [=](size_t Task, const Twine &ModuleName)
+ -> Expected<std::unique_ptr<CachedFileStream>> {
// Create the cache directory if not already done. Doing this lazily
// ensures the filesystem isn't mutated until the cache is.
if (std::error_code EC = sys::fs::create_directories(
// This CacheStream will move the temporary file into the cache when done.
return std::make_unique<CacheStream>(
std::make_unique<raw_fd_ostream>(Temp->FD, /* ShouldClose */ false),
- AddBuffer, std::move(*Temp), std::string(EntryPath.str()), Task);
+ AddBuffer, std::move(*Temp), std::string(EntryPath.str()),
+ ModuleName.str(), Task);
};
};
}
if (!CurrentActivity.empty())
OS << ' ' << CurrentActivity;
OS << ": ";
-
+
DiagnosticPrinterRawOStream DP(OS);
DI.print(DP);
OS << '\n';
-
+
if (DI.getSeverity() == DS_Error)
exit(1);
return true;
error("writing merged module failed.");
}
- auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ auto AddStream =
+ [&](size_t Task,
+ const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
std::string PartFilename = OutputFilename;
if (Parallelism != 1)
PartFilename += "." + utostr(Task);
if (HasErrors)
return 1;
- auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ auto AddStream =
+ [&](size_t Task,
+ const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
std::string Path = OutputFilename + "." + utostr(Task);
std::error_code EC;
return std::make_unique<CachedFileStream>(std::move(S), Path);
};
- auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- *AddStream(Task)->OS << MB->getBuffer();
+ auto AddBuffer = [&](size_t Task, const Twine &ModuleName,
+ std::unique_ptr<MemoryBuffer> MB) {
+ *AddStream(Task, ModuleName)->OS << MB->getBuffer();
};
FileCache Cache;