| //===- CodeGenDataReader.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 reading codegen data. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/CGData/CodeGenDataReader.h" |
| #include "llvm/CGData/OutlinedHashTreeRecord.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| |
| #define DEBUG_TYPE "cg-data-reader" |
| |
| using namespace llvm; |
| |
| namespace llvm { |
| |
| static Expected<std::unique_ptr<MemoryBuffer>> |
| setupMemoryBuffer(const Twine &Filename, vfs::FileSystem &FS) { |
| auto BufferOrErr = Filename.str() == "-" ? MemoryBuffer::getSTDIN() |
| : FS.getBufferForFile(Filename); |
| if (std::error_code EC = BufferOrErr.getError()) |
| return errorCodeToError(EC); |
| return std::move(BufferOrErr.get()); |
| } |
| |
| Error CodeGenDataReader::mergeFromObjectFile( |
| const object::ObjectFile *Obj, OutlinedHashTreeRecord &GlobalOutlineRecord, |
| StableFunctionMapRecord &GlobalFunctionMapRecord, |
| stable_hash *CombinedHash) { |
| Triple TT = Obj->makeTriple(); |
| auto CGOutlineName = |
| getCodeGenDataSectionName(CG_outline, TT.getObjectFormat(), false); |
| auto CGMergeName = |
| getCodeGenDataSectionName(CG_merge, TT.getObjectFormat(), false); |
| |
| auto processSectionContents = [&](const StringRef &Name, |
| const StringRef &Contents) { |
| if (Name != CGOutlineName && Name != CGMergeName) |
| return; |
| if (CombinedHash) |
| *CombinedHash = stable_hash_combine(*CombinedHash, xxh3_64bits(Contents)); |
| auto *Data = reinterpret_cast<const unsigned char *>(Contents.data()); |
| auto *EndData = Data + Contents.size(); |
| // In case dealing with an executable that has concatenated cgdata, |
| // we want to merge them into a single cgdata. |
| // Although it's not a typical workflow, we support this scenario |
| // by looping over all data in the sections. |
| if (Name == CGOutlineName) { |
| while (Data != EndData) { |
| OutlinedHashTreeRecord LocalOutlineRecord; |
| LocalOutlineRecord.deserialize(Data); |
| GlobalOutlineRecord.merge(LocalOutlineRecord); |
| } |
| } else if (Name == CGMergeName) { |
| while (Data != EndData) { |
| StableFunctionMapRecord LocalFunctionMapRecord; |
| LocalFunctionMapRecord.deserialize(Data); |
| GlobalFunctionMapRecord.merge(LocalFunctionMapRecord); |
| } |
| } |
| }; |
| |
| for (auto &Section : Obj->sections()) { |
| Expected<StringRef> NameOrErr = Section.getName(); |
| if (!NameOrErr) |
| return NameOrErr.takeError(); |
| Expected<StringRef> ContentsOrErr = Section.getContents(); |
| if (!ContentsOrErr) |
| return ContentsOrErr.takeError(); |
| processSectionContents(*NameOrErr, *ContentsOrErr); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error IndexedCodeGenDataReader::read() { |
| using namespace support; |
| |
| // The smallest header with the version 1 is 24 bytes. |
| // Do not update this value even with the new version of the header. |
| const unsigned MinHeaderSize = 24; |
| if (DataBuffer->getBufferSize() < MinHeaderSize) |
| return error(cgdata_error::bad_header); |
| |
| auto *Start = |
| reinterpret_cast<const unsigned char *>(DataBuffer->getBufferStart()); |
| auto *End = |
| reinterpret_cast<const unsigned char *>(DataBuffer->getBufferEnd()); |
| if (auto E = IndexedCGData::Header::readFromBuffer(Start).moveInto(Header)) |
| return E; |
| |
| if (hasOutlinedHashTree()) { |
| const unsigned char *Ptr = Start + Header.OutlinedHashTreeOffset; |
| if (Ptr >= End) |
| return error(cgdata_error::eof); |
| HashTreeRecord.deserialize(Ptr); |
| } |
| if (hasStableFunctionMap()) { |
| const unsigned char *Ptr = Start + Header.StableFunctionMapOffset; |
| if (Ptr >= End) |
| return error(cgdata_error::eof); |
| FunctionMapRecord.deserialize(Ptr); |
| } |
| |
| return success(); |
| } |
| |
| Expected<std::unique_ptr<CodeGenDataReader>> |
| CodeGenDataReader::create(const Twine &Path, vfs::FileSystem &FS) { |
| // Set up the buffer to read. |
| auto BufferOrError = setupMemoryBuffer(Path, FS); |
| if (Error E = BufferOrError.takeError()) |
| return std::move(E); |
| return CodeGenDataReader::create(std::move(BufferOrError.get())); |
| } |
| |
| Expected<std::unique_ptr<CodeGenDataReader>> |
| CodeGenDataReader::create(std::unique_ptr<MemoryBuffer> Buffer) { |
| if (Buffer->getBufferSize() == 0) |
| return make_error<CGDataError>(cgdata_error::empty_cgdata); |
| |
| std::unique_ptr<CodeGenDataReader> Reader; |
| // Create the reader. |
| if (IndexedCodeGenDataReader::hasFormat(*Buffer)) |
| Reader = std::make_unique<IndexedCodeGenDataReader>(std::move(Buffer)); |
| else if (TextCodeGenDataReader::hasFormat(*Buffer)) |
| Reader = std::make_unique<TextCodeGenDataReader>(std::move(Buffer)); |
| else |
| return make_error<CGDataError>(cgdata_error::malformed); |
| |
| // Initialize the reader and return the result. |
| if (Error E = Reader->read()) |
| return std::move(E); |
| |
| return std::move(Reader); |
| } |
| |
| bool IndexedCodeGenDataReader::hasFormat(const MemoryBuffer &DataBuffer) { |
| using namespace support; |
| if (DataBuffer.getBufferSize() < sizeof(IndexedCGData::Magic)) |
| return false; |
| |
| uint64_t Magic = endian::read<uint64_t, llvm::endianness::little, aligned>( |
| DataBuffer.getBufferStart()); |
| // Verify that it's magical. |
| return Magic == IndexedCGData::Magic; |
| } |
| |
| bool TextCodeGenDataReader::hasFormat(const MemoryBuffer &Buffer) { |
| // Verify that this really looks like plain ASCII text by checking a |
| // 'reasonable' number of characters (up to the magic size). |
| StringRef Prefix = Buffer.getBuffer().take_front(sizeof(uint64_t)); |
| return llvm::all_of(Prefix, [](char c) { return isPrint(c) || isSpace(c); }); |
| } |
| Error TextCodeGenDataReader::read() { |
| using namespace support; |
| |
| // Parse the custom header line by line. |
| for (; !Line.is_at_eof(); ++Line) { |
| // Skip empty or whitespace-only lines |
| if (Line->trim().empty()) |
| continue; |
| |
| if (!Line->starts_with(":")) |
| break; |
| StringRef Str = Line->drop_front().rtrim(); |
| if (Str.equals_insensitive("outlined_hash_tree")) |
| DataKind |= CGDataKind::FunctionOutlinedHashTree; |
| else if (Str.equals_insensitive("stable_function_map")) |
| DataKind |= CGDataKind::StableFunctionMergingMap; |
| else |
| return error(cgdata_error::bad_header); |
| } |
| |
| // We treat an empty header (that is a comment # only) as a valid header. |
| if (Line.is_at_eof()) { |
| if (DataKind == CGDataKind::Unknown) |
| return Error::success(); |
| return error(cgdata_error::bad_header); |
| } |
| |
| // The YAML docs follow after the header. |
| const char *Pos = Line->data(); |
| size_t Size = reinterpret_cast<size_t>(DataBuffer->getBufferEnd()) - |
| reinterpret_cast<size_t>(Pos); |
| yaml::Input YOS(StringRef(Pos, Size)); |
| if (hasOutlinedHashTree()) |
| HashTreeRecord.deserializeYAML(YOS); |
| if (hasStableFunctionMap()) |
| FunctionMapRecord.deserializeYAML(YOS); |
| |
| return Error::success(); |
| } |
| } // end namespace llvm |