|
5 | 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | 6 | // |
7 | 7 | //===----------------------------------------------------------------------===// |
8 | | -// |
9 | 8 | // \file |
10 | 9 | // This file implements support functions for Distributed ThinLTO, focusing on |
11 | | -// preparing input files for distribution. |
| 10 | +// preparing complilation jobs for distribution. |
12 | 11 | // |
13 | 12 | //===----------------------------------------------------------------------===// |
14 | 13 |
|
|
19 | 18 | #include "llvm/ADT/SmallString.h" |
20 | 19 | #include "llvm/ADT/StringExtras.h" |
21 | 20 | #include "llvm/ADT/StringRef.h" |
22 | | -#include "llvm/BinaryFormat/Magic.h" |
23 | 21 | #include "llvm/LTO/LTO.h" |
24 | | -#include "llvm/Object/Archive.h" |
25 | 22 | #include "llvm/Support/FileSystem.h" |
26 | | -#include "llvm/Support/JSON.h" |
27 | 23 | #include "llvm/Support/MemoryBufferRef.h" |
28 | 24 | #include "llvm/Support/Path.h" |
29 | 25 | #include "llvm/Support/Process.h" |
30 | 26 | #include "llvm/Support/TimeProfiler.h" |
31 | 27 | #include "llvm/Support/raw_ostream.h" |
32 | | -#ifdef _WIN32 |
33 | | -#include "llvm/Support/Windows/WindowsSupport.h" |
34 | | -#endif |
35 | 28 |
|
36 | 29 | #include <string> |
37 | 30 |
|
38 | 31 | using namespace llvm; |
39 | 32 |
|
40 | | -namespace { |
41 | | - |
42 | | -// Saves the content of Buffer to Path overwriting any existing file. |
43 | | -Error save(StringRef Buffer, StringRef Path) { |
44 | | - std::error_code EC; |
45 | | - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::OF_None); |
46 | | - if (EC) |
47 | | - return createStringError(inconvertibleErrorCode(), |
48 | | - "Failed to create file %s: %s", Path.data(), |
49 | | - EC.message().c_str()); |
50 | | - OS.write(Buffer.data(), Buffer.size()); |
51 | | - if (OS.has_error()) |
52 | | - return createStringError(inconvertibleErrorCode(), |
53 | | - "Failed writing to file %s", Path.data()); |
54 | | - return Error::success(); |
55 | | -} |
56 | | - |
57 | | -// Normalize and save a path. Aside from expanding Windows 8.3 short paths, |
58 | | -// no other normalization is currently required here. These paths are |
59 | | -// machine-local and break distribution systems; other normalization is |
60 | | -// handled by the DTLTO distributors. |
61 | | -Expected<StringRef> normalizePath(StringRef Path, StringSaver &Saver) { |
62 | | -#if defined(_WIN32) |
63 | | - if (Path.empty()) |
64 | | - return Path; |
65 | | - SmallString<256> Expanded; |
66 | | - if (std::error_code EC = sys::windows::makeLongFormPath(Path, Expanded)) |
67 | | - return createStringError(inconvertibleErrorCode(), |
68 | | - "Normalization failed for path %s: %s", |
69 | | - Path.str().c_str(), EC.message().c_str()); |
70 | | - return Saver.save(Expanded.str()); |
71 | | -#else |
72 | | - return Saver.save(Path); |
73 | | -#endif |
74 | | -} |
75 | | - |
76 | | -// Compute the file path for a thin archive member. |
77 | | -// |
78 | | -// For thin archives, an archive member name is typically a file path relative |
79 | | -// to the archive file's directory. This function resolves that path. |
80 | | -SmallString<256> computeThinArchiveMemberPath(StringRef ArchivePath, |
81 | | - StringRef MemberName) { |
82 | | - assert(!ArchivePath.empty() && "An archive file path must be non empty."); |
83 | | - SmallString<256> MemberPath; |
84 | | - if (sys::path::is_relative(MemberName)) { |
85 | | - MemberPath = sys::path::parent_path(ArchivePath); |
86 | | - sys::path::append(MemberPath, MemberName); |
87 | | - } else { |
88 | | - MemberPath = MemberName; |
89 | | - } |
90 | | - sys::path::remove_dots(MemberPath, /*remove_dot_dot=*/true); |
91 | | - return MemberPath; |
92 | | -} |
93 | | - |
94 | | -} // namespace |
95 | | - |
96 | | -// Determines if a file at the given path is a thin archive file. |
97 | | -Expected<bool> lto::DTLTO::isThinArchive(const StringRef ArchivePath) { |
98 | | - // Return cached result if available. |
99 | | - auto Cached = ArchiveIsThinCache.find(ArchivePath); |
100 | | - if (Cached != ArchiveIsThinCache.end()) |
101 | | - return Cached->second; |
102 | | - |
103 | | - uint64_t FileSize = -1; |
104 | | - std::error_code EC = sys::fs::file_size(ArchivePath, FileSize); |
105 | | - if (EC) |
106 | | - return createStringError(inconvertibleErrorCode(), |
107 | | - "Failed to get file size from archive %s: %s", |
108 | | - ArchivePath.data(), EC.message().c_str()); |
109 | | - if (FileSize < sizeof(object::ThinArchiveMagic)) |
110 | | - return createStringError(inconvertibleErrorCode(), |
111 | | - "Archive file size is too small %s", |
112 | | - ArchivePath.data()); |
113 | | - |
114 | | - // Read only the first few bytes containing the magic signature. |
115 | | - ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFileSlice( |
116 | | - ArchivePath, sizeof(object::ThinArchiveMagic), 0); |
117 | | - if ((EC = MBOrErr.getError())) |
118 | | - return createStringError(inconvertibleErrorCode(), |
119 | | - "Failed to read from archive %s: %s", |
120 | | - ArchivePath.data(), EC.message().c_str()); |
121 | | - |
122 | | - StringRef Buf = (*MBOrErr)->getBuffer(); |
123 | | - if (file_magic::archive != identify_magic(Buf)) |
124 | | - return createStringError(inconvertibleErrorCode(), |
125 | | - "Unknown format for archive %s", |
126 | | - ArchivePath.data()); |
127 | | - |
128 | | - bool IsThin = Buf.starts_with(object::ThinArchiveMagic); |
129 | | - |
130 | | - // Cache the result. |
131 | | - ArchiveIsThinCache[ArchivePath] = IsThin; |
132 | | - |
133 | | - return IsThin; |
134 | | -} |
135 | | - |
136 | | -// Add an input file and prepare it for distribution. |
137 | | -Expected<std::shared_ptr<lto::InputFile>> |
138 | | -lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) { |
139 | | - TimeTraceScope TimeScope("Add input for DTLTO"); |
140 | | - |
141 | | - // Add the input file to the LTO object. |
142 | | - InputFiles.emplace_back(InputPtr.release()); |
143 | | - auto &Input = InputFiles.back(); |
144 | | - BitcodeModule &BM = Input->getPrimaryBitcodeModule(); |
145 | | - |
146 | | - auto setIdFromPath = [&](StringRef Path) -> Error { |
147 | | - auto N = normalizePath(Path, Saver); |
148 | | - if (!N) |
149 | | - return N.takeError(); |
150 | | - BM.setModuleIdentifier(*N); |
151 | | - return Error::success(); |
152 | | - }; |
153 | | - |
154 | | - StringRef ArchivePath = Input->getArchivePath(); |
155 | | - |
156 | | - // In most cases, the module ID already points to an individual bitcode file |
157 | | - // on disk, so no further preparation for distribution is required. However, |
158 | | - // on Windows we overwite the module ID to expand Windows 8.3 short form |
159 | | - // paths. These paths are machine-local and break distribution systems; other |
160 | | - // normalization is handled by the DTLTO distributors. |
161 | | - if (ArchivePath.empty() && !Input->isFatLTOObject()) { |
162 | | -#if defined(_WIN32) |
163 | | - if (Error E = setIdFromPath(Input->getName())) |
164 | | - return std::move(E); |
165 | | -#endif |
166 | | - return Input; |
167 | | - } |
168 | | - |
169 | | - // For a member of a thin archive that is not a FatLTO object, there is an |
170 | | - // existing file on disk that can be used, so we can avoid having to |
171 | | - // serialize. |
172 | | - Expected<bool> UseThinMember = |
173 | | - Input->isFatLTOObject() ? false : isThinArchive(ArchivePath); |
174 | | - if (!UseThinMember) |
175 | | - return UseThinMember.takeError(); |
176 | | - if (*UseThinMember) { |
177 | | - // For thin archives, use the path to the actual member file on disk. |
178 | | - auto MemberPath = |
179 | | - computeThinArchiveMemberPath(ArchivePath, Input->getMemberName()); |
180 | | - if (Error E = setIdFromPath(MemberPath)) |
181 | | - return std::move(E); |
182 | | - return Input; |
183 | | - } |
184 | | - |
185 | | - // A new file on disk will be needed for archive members and FatLTO objects. |
186 | | - Input->setSerializeForDistribution(true); |
187 | | - |
188 | | - // Get the normalized output directory, if we haven't already. |
189 | | - if (LinkerOutputDir.empty()) { |
190 | | - auto N = normalizePath( |
191 | | - sys::path::parent_path(DistributorParams.LinkerOutputFile), Saver); |
192 | | - if (!N) |
193 | | - return N.takeError(); |
194 | | - LinkerOutputDir = *N; |
195 | | - } |
196 | | - |
197 | | - // Create a unique path by including the process ID and sequence number in the |
198 | | - // filename. |
199 | | - SmallString<256> Id(LinkerOutputDir); |
200 | | - sys::path::append(Id, |
201 | | - Twine(sys::path::filename(Input->getName())) + "." + |
202 | | - std::to_string(InputFiles.size()) /*Sequence number*/ + |
203 | | - "." + utohexstr(sys::Process::getProcessId()) + ".o"); |
204 | | - BM.setModuleIdentifier(Saver.save(Id.str())); |
205 | | - return Input; |
206 | | -} |
207 | | - |
208 | | -// Save the contents of ThinLTO-enabled input files that must be serialized for |
209 | | -// distribution. |
210 | | -Error lto::DTLTO::handleArchiveInputs() { |
211 | | - for (auto &Input : InputFiles) { |
212 | | - if (!Input->isThinLTO() || !Input->getSerializeForDistribution()) |
213 | | - continue; |
214 | | - // Save the content of the input file to a file named after the module ID. |
215 | | - StringRef ModuleId = Input->getName(); |
216 | | - TimeTraceScope TimeScope("Serialize bitcode input for DTLTO", ModuleId); |
217 | | - MemoryBufferRef Buf = Input->getFileBuffer(); |
218 | | - if (Error Err = save(Buf.getBuffer(), ModuleId)) |
219 | | - return Err; |
220 | | - // Cleanup this file on abnormal process exit. |
221 | | - if (!SaveTemps) |
222 | | - addToCleanup(ModuleId); |
223 | | - } |
224 | | - return Error::success(); |
225 | | -} |
226 | | - |
227 | 33 | // Remove temporary files created to enable distribution. |
228 | 34 | void lto::DTLTO::cleanup() { |
229 | 35 | if (!SaveTemps) { |
@@ -503,118 +309,3 @@ Error lto::DTLTO::addObjectFilesToLink() { |
503 | 309 | } |
504 | 310 | return Error::success(); |
505 | 311 | } |
506 | | - |
507 | | -// Generates a JSON file describing the backend compilations, for the |
508 | | -// distributor. |
509 | | -Error lto::DistributionDriver::emitJson() { |
510 | | - using json::Array; |
511 | | - std::error_code EC; |
512 | | - raw_fd_ostream OS(DistributorJsonFile, EC); |
513 | | - if (EC) |
514 | | - return createStringError(EC, "Error while creating Json file"); |
515 | | - |
516 | | - json::OStream JOS(OS); |
517 | | - JOS.object([&]() { |
518 | | - // Information common to all jobs. |
519 | | - JOS.attributeObject("common", [&]() { |
520 | | - JOS.attribute("linker_output", Params.LinkerOutputFile); |
521 | | - |
522 | | - JOS.attributeArray("args", [&]() { |
523 | | - JOS.value(Params.RemoteCompiler); |
524 | | - |
525 | | - // Forward any supplied prepend options. |
526 | | - if (!Params.RemoteCompilerPrependArgs.empty()) |
527 | | - for (auto &A : Params.RemoteCompilerPrependArgs) |
528 | | - JOS.value(A); |
529 | | - |
530 | | - JOS.value("-c"); |
531 | | - |
532 | | - JOS.value(std::string("--target=") + Params.TargetTriple.str()); |
533 | | - |
534 | | - for (const auto &A : Params.CodegenOptions) |
535 | | - JOS.value(A); |
536 | | - }); |
537 | | - |
538 | | - JOS.attribute("inputs", Array(Params.CommonInputs)); |
539 | | - }); |
540 | | - |
541 | | - // Per-compilation-job information. |
542 | | - JOS.attributeArray("jobs", [&]() { |
543 | | - for (const auto &J : Jobs) { |
544 | | - assert(J.Task != 0); |
545 | | - if (J.Cached) { |
546 | | - continue; |
547 | | - } |
548 | | - |
549 | | - SmallVector<StringRef, 2> Inputs; |
550 | | - SmallVector<StringRef, 1> Outputs; |
551 | | - |
552 | | - JOS.object([&]() { |
553 | | - JOS.attributeArray("args", [&]() { |
554 | | - JOS.value(J.ModuleID); |
555 | | - Inputs.push_back(J.ModuleID); |
556 | | - |
557 | | - JOS.value( |
558 | | - std::string("-fthinlto-index=" + J.SummaryIndexPath.str())); |
559 | | - Inputs.push_back(J.SummaryIndexPath); |
560 | | - |
561 | | - JOS.value("-o"); |
562 | | - JOS.value(J.NativeObjectPath); |
563 | | - Outputs.push_back(J.NativeObjectPath); |
564 | | - }); |
565 | | - |
566 | | - // Add the bitcode files from which imports will be made. These do |
567 | | - // not explicitly appear on the backend compilation command lines |
568 | | - // but are recorded in the summary index shards. |
569 | | - append_range(Inputs, J.ImportsFiles); |
570 | | - JOS.attribute("inputs", Array(Inputs)); |
571 | | - |
572 | | - JOS.attribute("outputs", Array(Outputs)); |
573 | | - }); |
574 | | - } |
575 | | - }); |
576 | | - }); |
577 | | - |
578 | | - return Error::success(); |
579 | | -} |
580 | | - |
581 | | -// Saves JSON file on a filesystem. |
582 | | -Error lto::DistributionDriver::saveJson() { |
583 | | - DistributorJsonFile = sys::path::parent_path(Params.LinkerOutputFile); |
584 | | - TimeTraceScope TimeScope("Emit DTLTO JSON"); |
585 | | - sys::path::append(DistributorJsonFile, |
586 | | - sys::path::stem(Params.LinkerOutputFile) + "." + |
587 | | - itostr(sys::Process::getProcessId()) + |
588 | | - ".dist-file.json"); |
589 | | - if (Error E = emitJson()) |
590 | | - return make_error<StringError>( |
591 | | - BCError + "failed to generate distributor JSON script: " + |
592 | | - DistributorJsonFile, |
593 | | - errorToErrorCode(std::move(E))); |
594 | | - |
595 | | - // Add JSON file to the cleanup files list. |
596 | | - if (!SaveTemps) |
597 | | - AddToCleanup(DistributorJsonFile); |
598 | | - return Error::success(); |
599 | | -} |
600 | | - |
601 | | -// Invokes the distributor to compile uncached ThinLTO modules remotely. |
602 | | -Error lto::DistributionDriver::operator()() { |
603 | | - if (Error E = saveJson()) |
604 | | - return E; |
605 | | - |
606 | | - TimeTraceScope TimeScope("Execute DTLTO distributor", Params.DistributorPath); |
607 | | - SmallVector<StringRef, 3> Args = {Params.DistributorPath}; |
608 | | - append_range(Args, Params.DistributorArgs); |
609 | | - Args.push_back(DistributorJsonFile); |
610 | | - std::string ErrMsg; |
611 | | - if (sys::ExecuteAndWait(Args[0], Args, |
612 | | - /*Env=*/std::nullopt, /*Redirects=*/{}, |
613 | | - /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg)) { |
614 | | - return make_error<StringError>( |
615 | | - BCError + "distributor execution failed" + |
616 | | - (!ErrMsg.empty() ? ": " + ErrMsg + Twine(".") : Twine(".")), |
617 | | - inconvertibleErrorCode()); |
618 | | - } |
619 | | - return Error::success(); |
620 | | -} |
0 commit comments