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(
114 CGDataOptions::Global.indexed_codegen_data_read_function_map_names);
115 if (LazyLoading)
116 FunctionMapRecord.lazyDeserialize(Buffer: std::move(SharedDataBuffer),
117 Offset: Header.StableFunctionMapOffset);
118 else
119 FunctionMapRecord.deserialize(Ptr);
120 }
121
122 return success();
123}
124
125Expected<std::unique_ptr<CodeGenDataReader>>
126CodeGenDataReader::create(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(Buffer: std::move(BufferOrError.get()), LazyLoading);
133}
134
135Expected<std::unique_ptr<CodeGenDataReader>>
136CodeGenDataReader::create(std::unique_ptr<MemoryBuffer> Buffer,
137 bool LazyLoading) {
138 if (Buffer->getBufferSize() == 0)
139 return make_error<CGDataError>(Args: cgdata_error::empty_cgdata);
140
141 std::unique_ptr<CodeGenDataReader> Reader;
142 // Create the reader.
143 if (IndexedCodeGenDataReader::hasFormat(Buffer: *Buffer))
144 Reader = std::make_unique<IndexedCodeGenDataReader>(args: std::move(Buffer),
145 args&: LazyLoading);
146 else if (TextCodeGenDataReader::hasFormat(Buffer: *Buffer))
147 Reader = std::make_unique<TextCodeGenDataReader>(args: std::move(Buffer));
148 else
149 return make_error<CGDataError>(Args: cgdata_error::malformed);
150
151 // Initialize the reader and return the result.
152 if (Error E = Reader->read())
153 return std::move(E);
154
155 return std::move(Reader);
156}
157
158bool IndexedCodeGenDataReader::hasFormat(const MemoryBuffer &DataBuffer) {
159 using namespace support;
160 if (DataBuffer.getBufferSize() < sizeof(IndexedCGData::Magic))
161 return false;
162
163 uint64_t Magic = endian::read<uint64_t, aligned>(memory: DataBuffer.getBufferStart(),
164 endian: llvm::endianness::little);
165 // Verify that it's magical.
166 return Magic == IndexedCGData::Magic;
167}
168
169bool TextCodeGenDataReader::hasFormat(const MemoryBuffer &Buffer) {
170 // Verify that this really looks like plain ASCII text by checking a
171 // 'reasonable' number of characters (up to the magic size).
172 StringRef Prefix = Buffer.getBuffer().take_front(N: sizeof(uint64_t));
173 return llvm::all_of(Range&: Prefix, P: [](char c) { return isPrint(C: c) || isSpace(C: c); });
174}
175Error TextCodeGenDataReader::read() {
176 using namespace support;
177
178 // Parse the custom header line by line.
179 for (; !Line.is_at_eof(); ++Line) {
180 // Skip empty or whitespace-only lines
181 if (Line->trim().empty())
182 continue;
183
184 if (!Line->starts_with(Prefix: ":"))
185 break;
186 StringRef Str = Line->drop_front().rtrim();
187 if (Str.equals_insensitive(RHS: "outlined_hash_tree"))
188 DataKind |= CGDataKind::FunctionOutlinedHashTree;
189 else if (Str.equals_insensitive(RHS: "stable_function_map"))
190 DataKind |= CGDataKind::StableFunctionMergingMap;
191 else
192 return error(Err: cgdata_error::bad_header);
193 }
194
195 // We treat an empty header (that is a comment # only) as a valid header.
196 if (Line.is_at_eof()) {
197 if (DataKind == CGDataKind::Unknown)
198 return Error::success();
199 return error(Err: cgdata_error::bad_header);
200 }
201
202 // The YAML docs follow after the header.
203 const char *Pos = Line->data();
204 size_t Size = reinterpret_cast<size_t>(DataBuffer->getBufferEnd()) -
205 reinterpret_cast<size_t>(Pos);
206 yaml::Input YOS(StringRef(Pos, Size));
207 if (hasOutlinedHashTree())
208 HashTreeRecord.deserializeYAML(YIS&: YOS);
209 if (hasStableFunctionMap())
210 FunctionMapRecord.deserializeYAML(YIS&: YOS);
211
212 return Error::success();
213}
214} // end namespace llvm
215