1 //===-Caching.cpp - LLVM Link Time Optimizer Cache Handling ---------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the Caching for ThinLTO.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/LTO/Caching.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/Support/Errc.h"
16 #include "llvm/Support/MemoryBuffer.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Process.h"
19 #include "llvm/Support/raw_ostream.h"
21 #if !defined(_MSC_VER) && !defined(__MINGW32__)
28 using namespace llvm::lto
;
30 Expected
<NativeObjectCache
> lto::localCache(StringRef CacheDirectoryPath
,
31 AddBufferFn AddBuffer
) {
32 if (std::error_code EC
= sys::fs::create_directories(CacheDirectoryPath
))
33 return errorCodeToError(EC
);
35 return [=](unsigned Task
, StringRef Key
) -> AddStreamFn
{
36 // This choice of file name allows the cache to be pruned (see pruneCache()
37 // in include/llvm/Support/CachePruning.h).
38 SmallString
<64> EntryPath
;
39 sys::path::append(EntryPath
, CacheDirectoryPath
, "llvmcache-" + Key
);
40 // First, see if we have a cache hit.
42 SmallString
<64> ResultPath
;
43 std::error_code EC
= sys::fs::openFileForRead(
44 Twine(EntryPath
), FD
, sys::fs::OF_UpdateAtime
, &ResultPath
);
46 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> MBOrErr
=
47 MemoryBuffer::getOpenFile(FD
, EntryPath
,
49 /*RequiresNullTerminator*/ false);
52 AddBuffer(Task
, std::move(*MBOrErr
));
55 EC
= MBOrErr
.getError();
58 // On Windows we can fail to open a cache file with a permission denied
59 // error. This generally means that another process has requested to delete
60 // the file while it is still open, but it could also mean that another
61 // process has opened the file without the sharing permissions we need.
62 // Since the file is probably being deleted we handle it in the same way as
63 // if the file did not exist at all.
64 if (EC
!= errc::no_such_file_or_directory
&& EC
!= errc::permission_denied
)
65 report_fatal_error(Twine("Failed to open cache file ") + EntryPath
+
66 ": " + EC
.message() + "\n");
68 // This native object stream is responsible for commiting the resulting
69 // file to the cache and calling AddBuffer to add it to the link.
70 struct CacheStream
: NativeObjectStream
{
71 AddBufferFn AddBuffer
;
72 sys::fs::TempFile TempFile
;
73 std::string EntryPath
;
76 CacheStream(std::unique_ptr
<raw_pwrite_stream
> OS
, AddBufferFn AddBuffer
,
77 sys::fs::TempFile TempFile
, std::string EntryPath
,
79 : NativeObjectStream(std::move(OS
)), AddBuffer(std::move(AddBuffer
)),
80 TempFile(std::move(TempFile
)), EntryPath(std::move(EntryPath
)),
84 // Make sure the stream is closed before committing it.
87 // Open the file first to avoid racing with a cache pruner.
88 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> MBOrErr
=
89 MemoryBuffer::getOpenFile(TempFile
.FD
, TempFile
.TmpName
,
91 /*RequiresNullTerminator*/ false);
93 report_fatal_error(Twine("Failed to open new cache file ") +
94 TempFile
.TmpName
+ ": " +
95 MBOrErr
.getError().message() + "\n");
97 // On POSIX systems, this will atomically replace the destination if
98 // it already exists. We try to emulate this on Windows, but this may
99 // fail with a permission denied error (for example, if the destination
100 // is currently opened by another process that does not give us the
101 // sharing permissions we need). Since the existing file should be
102 // semantically equivalent to the one we are trying to write, we give
103 // AddBuffer a copy of the bytes we wrote in that case. We do this
104 // instead of just using the existing file, because the pruner might
105 // delete the file before we get a chance to use it.
106 Error E
= TempFile
.keep(EntryPath
);
107 E
= handleErrors(std::move(E
), [&](const ECError
&E
) -> Error
{
108 std::error_code EC
= E
.convertToErrorCode();
109 if (EC
!= errc::permission_denied
)
110 return errorCodeToError(EC
);
112 auto MBCopy
= MemoryBuffer::getMemBufferCopy((*MBOrErr
)->getBuffer(),
114 MBOrErr
= std::move(MBCopy
);
116 // FIXME: should we consume the discard error?
117 consumeError(TempFile
.discard());
119 return Error::success();
123 report_fatal_error(Twine("Failed to rename temporary file ") +
124 TempFile
.TmpName
+ " to " + EntryPath
+ ": " +
125 toString(std::move(E
)) + "\n");
127 AddBuffer(Task
, std::move(*MBOrErr
));
131 return [=](size_t Task
) -> std::unique_ptr
<NativeObjectStream
> {
132 // Write to a temporary to avoid race condition
133 SmallString
<64> TempFilenameModel
;
134 sys::path::append(TempFilenameModel
, CacheDirectoryPath
, "Thin-%%%%%%.tmp.o");
135 Expected
<sys::fs::TempFile
> Temp
= sys::fs::TempFile::create(
136 TempFilenameModel
, sys::fs::owner_read
| sys::fs::owner_write
);
138 errs() << "Error: " << toString(Temp
.takeError()) << "\n";
139 report_fatal_error("ThinLTO: Can't get a temporary file");
142 // This CacheStream will move the temporary file into the cache when done.
143 return llvm::make_unique
<CacheStream
>(
144 llvm::make_unique
<raw_fd_ostream
>(Temp
->FD
, /* ShouldClose */ false),
145 AddBuffer
, std::move(*Temp
), EntryPath
.str(), Task
);