1//===- CodeGenDataReader.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 reading codegen data.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/CGData/CodeGenDataReader.h"
14#include "CGDataOptions.h"
15#include "llvm/CGData/OutlinedHashTreeRecord.h"
16#include "llvm/Object/ObjectFile.h"
17#include "llvm/Support/MemoryBuffer.h"
18
19#define DEBUG_TYPE "cg-data-reader"
20
21using namespace llvm;
22
23namespace llvm {
24
25static Expected<std::unique_ptr<MemoryBuffer>>
26setupMemoryBuffer(const Twine &Filename, vfs::FileSystem &FS) {
27 auto BufferOrErr = Filename.str() == "-" ? MemoryBuffer::getSTDIN()
28 : FS.getBufferForFile(Name: Filename);
29 if (std::error_code EC = BufferOrErr.getError())
30 return errorCodeToError(EC);
31 return std::move(BufferOrErr.get());
32}
33
34Error CodeGenDataReader::mergeFromObjectFile(
35 const object::ObjectFile *Obj, OutlinedHashTreeRecord &GlobalOutlineRecord,
36 StableFunctionMapRecord &GlobalFunctionMapRecord,
37 stable_hash *CombinedHash) {
38 Triple::ObjectFormatType OF = Obj->getTripleObjectFormat();
39 auto CGOutlineName =
40 getCodeGenDataSectionName(CGSK: CG_outline, OF, /*AddSegmentInfo=*/false);
41 auto CGMergeName =
42 getCodeGenDataSectionName(CGSK: CG_merge, OF, /*AddSegmentInfo=*/false);
43
44 auto processSectionContents = [&](const StringRef &Name,
45 const StringRef &Contents) {
46 if (Name != CGOutlineName && Name != CGMergeName)
47 return;
48 if (CombinedHash)
49 *CombinedHash = stable_hash_combine(A: *CombinedHash, B: xxh3_64bits(data: Contents));
50 auto *Data = reinterpret_cast<const unsigned char *>(Contents.data());
51 auto *EndData = Data + Contents.size();
52 // In case dealing with an executable that has concatenated cgdata,
53 // we want to merge them into a single cgdata.
54 // Although it's not a typical workflow, we support this scenario
55 // by looping over all data in the sections.
56 if (Name == CGOutlineName) {
57 while (Data != EndData) {
58 OutlinedHashTreeRecord LocalOutlineRecord;
59 LocalOutlineRecord.deserialize(Ptr&: Data);
60 GlobalOutlineRecord.merge(Other: LocalOutlineRecord);
61 }
62 } else if (Name == CGMergeName) {
63 while (Data != EndData) {
64 StableFunctionMapRecord LocalFunctionMapRecord;
65 LocalFunctionMapRecord.deserialize(Ptr&: Data);
66 GlobalFunctionMapRecord.merge(Other: LocalFunctionMapRecord);
67 }
68 }
69 };
70
71 for (auto &Section : Obj->sections()) {
72 Expected<StringRef> NameOrErr = Section.getName();
73 if (!NameOrErr)
74 return NameOrErr.takeError();
75 Expected<StringRef> ContentsOrErr = Section.getContents();
76 if (!ContentsOrErr)
77 return ContentsOrErr.takeError();
78 processSectionContents(*NameOrErr, *ContentsOrErr);
79 }
80
81 return Error::success();
82}
83
84Error IndexedCodeGenDataReader::read() {
85 using namespace support;
86
87 // The smallest header with the version 1 is 24 bytes.
88 // Do not update this value even with the new version of the header.
89 const unsigned MinHeaderSize = 24;
90 if (DataBuffer->getBufferSize() < MinHeaderSize)
91 return error(Err: cgdata_error::bad_header);
92
93 auto *Start =
94 reinterpret_cast<const unsigned char *>(DataBuffer->getBufferStart());
95 auto *End =
96 reinterpret_cast<const unsigned char *>(DataBuffer->getBufferEnd());
97 if (auto E = IndexedCGData::Header::readFromBuffer(Curr: Start).moveInto(Value&: Header))
98 return E;
99
100 if (hasOutlinedHashTree()) {
101 const unsigned char *Ptr = Start + Header.OutlinedHashTreeOffset;
102 if (Ptr >= End)
103 return error(Err: cgdata_error::eof);
104 HashTreeRecord.deserialize(Ptr);
105 }
106
107 // TODO: lazy loading support for outlined hash tree.
108 std::shared_ptr<MemoryBuffer> SharedDataBuffer = std::move(DataBuffer);
109 if (hasStableFunctionMap()) {
110 const unsigned char *Ptr = Start + Header.StableFunctionMapOffset;
111 if (Ptr >= End)
112 return error(Err: cgdata_error::eof);
113 FunctionMapRecord.setReadStableFunctionMapNames(ReadStableFunctionMapNames);
114 if (LazyLoading)
115 FunctionMapRecord.lazyDeserialize(Buffer: std::move(SharedDataBuffer),
116 Offset: Header.StableFunctionMapOffset);
117 else
118 FunctionMapRecord.deserialize(Ptr);
119 }
120
121 return success();
122}
123
124Expected<std::unique_ptr<CodeGenDataReader>>
125CodeGenDataReader::create(const cl::LibraryOptionsContext &LibraryOptions,
126 const Twine &Path, vfs::FileSystem &FS,
127 bool LazyLoading) {
128 // Set up the buffer to read.
129 auto BufferOrError = setupMemoryBuffer(Filename: Path, FS);
130 if (Error E = BufferOrError.takeError())
131 return std::move(E);
132 return CodeGenDataReader::create(LibraryOptions,
133 Buffer: std::move(BufferOrError.get()), LazyLoading);
134}
135
136Expected<std::unique_ptr<CodeGenDataReader>>
137CodeGenDataReader::create(const cl::LibraryOptionsContext &LibraryOptions,
138 std::unique_ptr<MemoryBuffer> Buffer,
139 bool LazyLoading) {
140 if (Buffer->getBufferSize() == 0)
141 return make_error<CGDataError>(Args: cgdata_error::empty_cgdata);
142
143 std::unique_ptr<CodeGenDataReader> Reader;
144 // Create the reader.
145 if (IndexedCodeGenDataReader::hasFormat(Buffer: *Buffer))
146 Reader = std::make_unique<IndexedCodeGenDataReader>(
147 args: LibraryOptions.get<CGDataOptions>()
148 .indexed_codegen_data_read_function_map_names,
149 args: std::move(Buffer), args&: LazyLoading);
150 else if (TextCodeGenDataReader::hasFormat(Buffer: *Buffer))
151 Reader = std::make_unique<TextCodeGenDataReader>(args: std::move(Buffer));
152 else
153 return make_error<CGDataError>(Args: cgdata_error::malformed);
154
155 // Initialize the reader and return the result.
156 if (Error E = Reader->read())
157 return std::move(E);
158
159 return std::move(Reader);
160}
161
162bool IndexedCodeGenDataReader::hasFormat(const MemoryBuffer &DataBuffer) {
163 using namespace support;
164 if (DataBuffer.getBufferSize() < sizeof(IndexedCGData::Magic))
165 return false;
166
167 uint64_t Magic = endian::read<uint64_t, aligned>(memory: DataBuffer.getBufferStart(),
168 endian: llvm::endianness::little);
169 // Verify that it's magical.
170 return Magic == IndexedCGData::Magic;
171}
172
173bool TextCodeGenDataReader::hasFormat(const MemoryBuffer &Buffer) {
174 // Verify that this really looks like plain ASCII text by checking a
175 // 'reasonable' number of characters (up to the magic size).
176 StringRef Prefix = Buffer.getBuffer().take_front(N: sizeof(uint64_t));
177 return llvm::all_of(Range&: Prefix, P: [](char c) { return isPrint(C: c) || isSpace(C: c); });
178}
179Error TextCodeGenDataReader::read() {
180 using namespace support;
181
182 // Parse the custom header line by line.
183 for (; !Line.is_at_eof(); ++Line) {
184 // Skip empty or whitespace-only lines
185 if (Line->trim().empty())
186 continue;
187
188 if (!Line->starts_with(Prefix: ":"))
189 break;
190 StringRef Str = Line->drop_front().rtrim();
191 if (Str.equals_insensitive(RHS: "outlined_hash_tree"))
192 DataKind |= CGDataKind::FunctionOutlinedHashTree;
193 else if (Str.equals_insensitive(RHS: "stable_function_map"))
194 DataKind |= CGDataKind::StableFunctionMergingMap;
195 else
196 return error(Err: cgdata_error::bad_header);
197 }
198
199 // We treat an empty header (that is a comment # only) as a valid header.
200 if (Line.is_at_eof()) {
201 if (DataKind == CGDataKind::Unknown)
202 return Error::success();
203 return error(Err: cgdata_error::bad_header);
204 }
205
206 // The YAML docs follow after the header.
207 const char *Pos = Line->data();
208 size_t Size = reinterpret_cast<size_t>(DataBuffer->getBufferEnd()) -
209 reinterpret_cast<size_t>(Pos);
210 yaml::Input YOS(StringRef(Pos, Size));
211 if (hasOutlinedHashTree())
212 HashTreeRecord.deserializeYAML(YIS&: YOS);
213 if (hasStableFunctionMap())
214 FunctionMapRecord.deserializeYAML(YIS&: YOS);
215
216 return Error::success();
217}
218} // end namespace llvm
219