1//===-- CodeGenData.cpp ---------------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains support for codegen data that has stable summary which
10// can be used to optimize the code in the subsequent codegen.
11//
12//===----------------------------------------------------------------------===//
13
14#include "CGDataOptions.h"
15#include "llvm/Bitcode/BitcodeWriter.h"
16#include "llvm/CGData/CodeGenDataReader.h"
17#include "llvm/CGData/OutlinedHashTreeRecord.h"
18#include "llvm/CGData/StableFunctionMapRecord.h"
19#include "llvm/Object/ObjectFile.h"
20#include "llvm/Support/Caching.h"
21#include "llvm/Support/WithColor.h"
22
23#define DEBUG_TYPE "cg-data"
24
25using namespace llvm;
26using namespace cgdata;
27
28static std::string getCGDataErrString(cgdata_error Err,
29 const std::string &ErrMsg = "") {
30 std::string Msg;
31 raw_string_ostream OS(Msg);
32
33 switch (Err) {
34 case cgdata_error::success:
35 OS << "success";
36 break;
37 case cgdata_error::eof:
38 OS << "end of File";
39 break;
40 case cgdata_error::bad_magic:
41 OS << "invalid codegen data (bad magic)";
42 break;
43 case cgdata_error::bad_header:
44 OS << "invalid codegen data (file header is corrupt)";
45 break;
46 case cgdata_error::empty_cgdata:
47 OS << "empty codegen data";
48 break;
49 case cgdata_error::malformed:
50 OS << "malformed codegen data";
51 break;
52 case cgdata_error::unsupported_version:
53 OS << "unsupported codegen data version";
54 break;
55 }
56
57 // If optional error message is not empty, append it to the message.
58 if (!ErrMsg.empty())
59 OS << ": " << ErrMsg;
60
61 return OS.str();
62}
63
64namespace {
65
66// FIXME: This class is only here to support the transition to llvm::Error. It
67// will be removed once this transition is complete. Clients should prefer to
68// deal with the Error value directly, rather than converting to error_code.
69class CGDataErrorCategoryType : public std::error_category {
70 const char *name() const noexcept override { return "llvm.cgdata"; }
71
72 std::string message(int IE) const override {
73 return getCGDataErrString(Err: static_cast<cgdata_error>(IE));
74 }
75};
76
77} // end anonymous namespace
78
79const std::error_category &llvm::cgdata_category() {
80 static CGDataErrorCategoryType ErrorCategory;
81 return ErrorCategory;
82}
83
84std::string CGDataError::message() const {
85 return getCGDataErrString(Err, ErrMsg: Msg);
86}
87
88char CGDataError::ID = 0;
89
90namespace {
91
92const char *CodeGenDataSectNameCommon[] = {
93#define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) \
94 SectNameCommon,
95#include "llvm/CGData/CodeGenData.inc"
96};
97
98const char *CodeGenDataSectNameCoff[] = {
99#define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) \
100 SectNameCoff,
101#include "llvm/CGData/CodeGenData.inc"
102};
103
104const char *CodeGenDataSectNamePrefix[] = {
105#define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) Prefix,
106#include "llvm/CGData/CodeGenData.inc"
107};
108
109} // namespace
110
111bool llvm::cgdata::thinLTOTwoRounds(
112 const cl::LibraryOptionsContext &LibraryOptions) {
113 return LibraryOptions.get<CGDataOptions>().codegen_data_thinlto_two_rounds;
114}
115
116namespace llvm {
117
118std::string getCodeGenDataSectionName(CGDataSectKind CGSK,
119 Triple::ObjectFormatType OF,
120 bool AddSegmentInfo) {
121 std::string SectName;
122
123 if (OF == Triple::MachO && AddSegmentInfo)
124 SectName = CodeGenDataSectNamePrefix[CGSK];
125
126 if (OF == Triple::COFF)
127 SectName += CodeGenDataSectNameCoff[CGSK];
128 else
129 SectName += CodeGenDataSectNameCommon[CGSK];
130
131 return SectName;
132}
133
134std::unique_ptr<CodeGenData> CodeGenData::Instance = nullptr;
135std::once_flag CodeGenData::OnceFlag;
136
137CodeGenData &
138CodeGenData::getInstance(const cl::LibraryOptionsContext &LibraryOptions) {
139 std::call_once(once&: CodeGenData::OnceFlag, f: [&LibraryOptions]() {
140 Instance = std::unique_ptr<CodeGenData>(new CodeGenData());
141
142 const CGDataOptions &Opts = LibraryOptions.get<CGDataOptions>();
143 if (Opts.codegen_data_generate || Opts.codegen_data_thinlto_two_rounds)
144 Instance->EmitCGData = true;
145 else if (!Opts.codegen_data_use_path.empty()) {
146 // Initialize the global CGData if the input file name is given.
147 // We do not error-out when failing to parse the input file.
148 // Instead, just emit an warning message and fall back as if no CGData
149 // were available.
150 auto FS = vfs::getRealFileSystem();
151 auto ReaderOrErr = CodeGenDataReader::create(
152 LibraryOptions, Path: Opts.codegen_data_use_path, FS&: *FS,
153 LazyLoading: Opts.indexed_codegen_data_lazy_loading);
154 if (Error E = ReaderOrErr.takeError()) {
155 warn(E: std::move(E), Whence: Opts.codegen_data_use_path);
156 return;
157 }
158 // Publish each CGData based on the data type in the header.
159 auto Reader = ReaderOrErr->get();
160 if (Reader->hasOutlinedHashTree())
161 Instance->publishOutlinedHashTree(HashTree: Reader->releaseOutlinedHashTree());
162 if (Reader->hasStableFunctionMap())
163 Instance->publishStableFunctionMap(FunctionMap: Reader->releaseStableFunctionMap());
164 }
165 });
166 return *Instance;
167}
168
169namespace IndexedCGData {
170
171Expected<Header> Header::readFromBuffer(const unsigned char *Curr) {
172 using namespace support;
173
174 static_assert(std::is_standard_layout_v<llvm::IndexedCGData::Header>,
175 "The header should be standard layout type since we use offset "
176 "of fields to read.");
177 Header H;
178 H.Magic = endian::readNext<uint64_t, endianness::little, unaligned>(memory&: Curr);
179 if (H.Magic != IndexedCGData::Magic)
180 return make_error<CGDataError>(Args: cgdata_error::bad_magic);
181 H.Version = endian::readNext<uint32_t, endianness::little, unaligned>(memory&: Curr);
182 if (H.Version > IndexedCGData::CGDataVersion::CurrentVersion)
183 return make_error<CGDataError>(Args: cgdata_error::unsupported_version);
184 H.DataKind = endian::readNext<uint32_t, endianness::little, unaligned>(memory&: Curr);
185
186 static_assert(IndexedCGData::CGDataVersion::CurrentVersion == Version4,
187 "Please update the offset computation below if a new field has "
188 "been added to the header.");
189 H.OutlinedHashTreeOffset =
190 endian::readNext<uint64_t, endianness::little, unaligned>(memory&: Curr);
191 if (H.Version >= 2)
192 H.StableFunctionMapOffset =
193 endian::readNext<uint64_t, endianness::little, unaligned>(memory&: Curr);
194
195 return H;
196}
197
198} // end namespace IndexedCGData
199
200namespace cgdata {
201
202void warn(Twine Message, StringRef Whence, StringRef Hint) {
203 WithColor::warning();
204 if (!Whence.empty())
205 errs() << Whence << ": ";
206 errs() << Message << "\n";
207 if (!Hint.empty())
208 WithColor::note() << Hint << "\n";
209}
210
211void warn(Error E, StringRef Whence) {
212 if (E.isA<CGDataError>()) {
213 handleAllErrors(E: std::move(E), Handlers: [&](const CGDataError &IPE) {
214 warn(Message: IPE.message(), Whence, Hint: "");
215 });
216 }
217}
218
219void saveModuleForTwoRounds(const Module &TheModule, unsigned Task,
220 AddStreamFn AddStream) {
221 LLVM_DEBUG(dbgs() << "Saving module: " << TheModule.getModuleIdentifier()
222 << " in Task " << Task << "\n");
223 Expected<std::unique_ptr<CachedFileStream>> StreamOrErr =
224 AddStream(Task, TheModule.getModuleIdentifier());
225 if (Error Err = StreamOrErr.takeError())
226 report_fatal_error(Err: std::move(Err));
227 std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;
228
229 WriteBitcodeToFile(M: TheModule, Out&: *Stream->OS,
230 /*ShouldPreserveUseListOrder=*/true);
231
232 if (Error Err = Stream->commit())
233 report_fatal_error(Err: std::move(Err));
234}
235
236std::unique_ptr<Module> loadModuleForTwoRounds(BitcodeModule &OrigModule,
237 unsigned Task,
238 LLVMContext &Context,
239 ArrayRef<StringRef> IRFiles) {
240 LLVM_DEBUG(dbgs() << "Loading module: " << OrigModule.getModuleIdentifier()
241 << " in Task " << Task << "\n");
242 auto FileBuffer = MemoryBuffer::getMemBuffer(
243 InputData: IRFiles[Task], BufferName: "in-memory IR file", /*RequiresNullTerminator=*/false);
244 auto RestoredModule = parseBitcodeFile(Buffer: *FileBuffer, Context);
245 if (!RestoredModule)
246 report_fatal_error(
247 reason: Twine("Failed to parse optimized bitcode loaded for Task: ") +
248 Twine(Task) + "\n");
249
250 // Restore the original module identifier.
251 (*RestoredModule)->setModuleIdentifier(OrigModule.getModuleIdentifier());
252 return std::move(*RestoredModule);
253}
254
255Expected<stable_hash>
256mergeCodeGenData(const cl::LibraryOptionsContext &LibraryOptions,
257 ArrayRef<StringRef> ObjFiles) {
258 OutlinedHashTreeRecord GlobalOutlineRecord;
259 StableFunctionMapRecord GlobalStableFunctionMapRecord;
260 stable_hash CombinedHash = 0;
261 for (auto File : ObjFiles) {
262 if (File.empty())
263 continue;
264 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(
265 InputData: File, BufferName: "in-memory object file", /*RequiresNullTerminator=*/false);
266 Expected<std::unique_ptr<object::ObjectFile>> BinOrErr =
267 object::ObjectFile::createObjectFile(Object: Buffer->getMemBufferRef());
268 if (!BinOrErr)
269 return BinOrErr.takeError();
270
271 std::unique_ptr<object::ObjectFile> &Obj = BinOrErr.get();
272 if (auto E = CodeGenDataReader::mergeFromObjectFile(
273 Obj: Obj.get(), GlobalOutlineRecord, GlobalFunctionMapRecord&: GlobalStableFunctionMapRecord,
274 CombinedHash: &CombinedHash))
275 return E;
276 }
277
278 GlobalStableFunctionMapRecord.finalize(LibraryOptions);
279
280 if (!GlobalOutlineRecord.empty())
281 cgdata::publishOutlinedHashTree(LibraryOptions,
282 HashTree: std::move(GlobalOutlineRecord.HashTree));
283 if (!GlobalStableFunctionMapRecord.empty())
284 cgdata::publishStableFunctionMap(
285 LibraryOptions, FunctionMap: std::move(GlobalStableFunctionMapRecord.FunctionMap));
286
287 return CombinedHash;
288}
289
290} // end namespace cgdata
291
292} // end namespace llvm
293