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