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