| //===- CodeGenDataWriter.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 writing codegen data. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CGData/CodeGenDataWriter.h" | |
| #define DEBUG_TYPE "cg-data-writer" | |
| using namespace llvm; | |
| void CGDataOStream::patch(ArrayRef<CGDataPatchItem> P) { | |
| using namespace support; | |
| switch (Kind) { | |
| case OStreamKind::fd: { | |
| raw_fd_ostream &FDOStream = static_cast<raw_fd_ostream &>(OS); | |
| const uint64_t LastPos = FDOStream.tell(); | |
| for (const auto &K : P) { | |
| FDOStream.seek(K.Pos); | |
| for (size_t I = 0; I < K.D.size(); ++I) | |
| write(K.D[I]); | |
| } | |
| // Reset the stream to the last position after patching so that users | |
| // don't accidentally overwrite data. This makes it consistent with | |
| // the string stream below which replaces the data directly. | |
| FDOStream.seek(LastPos); | |
| break; | |
| } | |
| case OStreamKind::string: { | |
| raw_string_ostream &SOStream = static_cast<raw_string_ostream &>(OS); | |
| std::string &Data = SOStream.str(); // with flush | |
| for (const auto &K : P) { | |
| for (size_t I = 0; I < K.D.size(); ++I) { | |
| uint64_t Bytes = | |
| endian::byte_swap<uint64_t>(K.D[I], llvm::endianness::little); | |
| Data.replace(K.Pos + I * sizeof(uint64_t), sizeof(uint64_t), | |
| reinterpret_cast<const char *>(&Bytes), sizeof(uint64_t)); | |
| } | |
| } | |
| break; | |
| } | |
| case OStreamKind::svector: { | |
| raw_svector_ostream &VOStream = static_cast<raw_svector_ostream &>(OS); | |
| for (const auto &K : P) { | |
| for (size_t I = 0; I < K.D.size(); ++I) { | |
| uint64_t Bytes = | |
| endian::byte_swap<uint64_t>(K.D[I], llvm::endianness::little); | |
| VOStream.pwrite(reinterpret_cast<const char *>(&Bytes), | |
| sizeof(uint64_t), K.Pos + I * sizeof(uint64_t)); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| void CodeGenDataWriter::addRecord(OutlinedHashTreeRecord &Record) { | |
| assert(Record.HashTree && "empty hash tree in the record"); | |
| HashTreeRecord.HashTree = std::move(Record.HashTree); | |
| DataKind |= CGDataKind::FunctionOutlinedHashTree; | |
| } | |
| void CodeGenDataWriter::addRecord(StableFunctionMapRecord &Record) { | |
| assert(Record.FunctionMap && "empty function map in the record"); | |
| FunctionMapRecord.FunctionMap = std::move(Record.FunctionMap); | |
| DataKind |= CGDataKind::StableFunctionMergingMap; | |
| } | |
| Error CodeGenDataWriter::write(raw_fd_ostream &OS) { | |
| CGDataOStream COS(OS); | |
| return writeImpl(COS); | |
| } | |
| Error CodeGenDataWriter::writeHeader(CGDataOStream &COS) { | |
| using namespace support; | |
| IndexedCGData::Header Header; | |
| Header.Magic = IndexedCGData::Magic; | |
| Header.Version = IndexedCGData::Version; | |
| // Set the CGDataKind depending on the kind. | |
| Header.DataKind = 0; | |
| if (static_cast<bool>(DataKind & CGDataKind::FunctionOutlinedHashTree)) | |
| Header.DataKind |= | |
| static_cast<uint32_t>(CGDataKind::FunctionOutlinedHashTree); | |
| if (static_cast<bool>(DataKind & CGDataKind::StableFunctionMergingMap)) | |
| Header.DataKind |= | |
| static_cast<uint32_t>(CGDataKind::StableFunctionMergingMap); | |
| Header.OutlinedHashTreeOffset = 0; | |
| Header.StableFunctionMapOffset = 0; | |
| // Only write up to the CGDataKind. We need to remember the offset of the | |
| // remaining fields to allow back-patching later. | |
| COS.write(Header.Magic); | |
| COS.write32(Header.Version); | |
| COS.write32(Header.DataKind); | |
| // Save the location of Header.OutlinedHashTreeOffset field in \c COS. | |
| OutlinedHashTreeOffset = COS.tell(); | |
| // Reserve the space for OutlinedHashTreeOffset field. | |
| COS.write(0); | |
| // Save the location of Header.StableFunctionMapOffset field in \c COS. | |
| StableFunctionMapOffset = COS.tell(); | |
| // Reserve the space for StableFunctionMapOffset field. | |
| COS.write(0); | |
| return Error::success(); | |
| } | |
| Error CodeGenDataWriter::writeImpl(CGDataOStream &COS) { | |
| if (Error E = writeHeader(COS)) | |
| return E; | |
| std::vector<CGDataPatchItem> PatchItems; | |
| uint64_t OutlinedHashTreeFieldStart = COS.tell(); | |
| if (hasOutlinedHashTree()) | |
| HashTreeRecord.serialize(COS.OS); | |
| uint64_t StableFunctionMapFieldStart = COS.tell(); | |
| if (hasStableFunctionMap()) | |
| FunctionMapRecord.serialize(COS.OS, PatchItems); | |
| // Back patch the offsets. | |
| PatchItems.emplace_back(OutlinedHashTreeOffset, &OutlinedHashTreeFieldStart, | |
| 1); | |
| PatchItems.emplace_back(StableFunctionMapOffset, &StableFunctionMapFieldStart, | |
| 1); | |
| COS.patch(PatchItems); | |
| return Error::success(); | |
| } | |
| Error CodeGenDataWriter::writeHeaderText(raw_fd_ostream &OS) { | |
| if (hasOutlinedHashTree()) | |
| OS << "# Outlined stable hash tree\n:outlined_hash_tree\n"; | |
| if (hasStableFunctionMap()) | |
| OS << "# Stable function map\n:stable_function_map\n"; | |
| // TODO: Add more data types in this header | |
| return Error::success(); | |
| } | |
| Error CodeGenDataWriter::writeText(raw_fd_ostream &OS) { | |
| if (Error E = writeHeaderText(OS)) | |
| return E; | |
| yaml::Output YOS(OS); | |
| if (hasOutlinedHashTree()) | |
| HashTreeRecord.serializeYAML(YOS); | |
| if (hasStableFunctionMap()) | |
| FunctionMapRecord.serializeYAML(YOS); | |
| // TODO: Write more yaml cgdata in order | |
| return Error::success(); | |
| } |