//===- 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/CodeGenData/CodeGenDataWriter.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" #define DEBUG_TYPE "cg-data-writer" using namespace llvm; namespace llvm { /// A struct to define how the data stream should be patched. struct CGDataPatchItem { uint64_t Pos; // Where to patch. uint64_t *D; // Pointer to an array of source data. int N; // Number of elements in \c D array. }; // A wrapper class to abstract writer stream with support of bytes // back patching. class CGDataOStream { public: CGDataOStream(raw_fd_ostream &FD) : IsFDOStream(true), OS(FD), LE(FD, llvm::endianness::little) {} CGDataOStream(raw_string_ostream &STR) : IsFDOStream(false), OS(STR), LE(STR, llvm::endianness::little) {} uint64_t tell() { return OS.tell(); } void write(uint64_t V) { LE.write(V); } void write32(uint32_t V) { LE.write(V); } void write8(uint8_t V) { LE.write(V); } // \c patch can only be called when all data is written and flushed. // For raw_string_ostream, the patch is done on the target string // directly and it won't be reflected in the stream's internal buffer. void patch(ArrayRef P) { using namespace support; if (IsFDOStream) { raw_fd_ostream &FDOStream = static_cast(OS); const uint64_t LastPos = FDOStream.tell(); for (const auto &K : P) { FDOStream.seek(K.Pos); for (int I = 0; I < K.N; 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); } else { raw_string_ostream &SOStream = static_cast(OS); std::string &Data = SOStream.str(); // with flush for (const auto &K : P) { for (int I = 0; I < K.N; I++) { uint64_t Bytes = endian::byte_swap(K.D[I]); Data.replace(K.Pos + I * sizeof(uint64_t), sizeof(uint64_t), (const char *)&Bytes, sizeof(uint64_t)); } } } } // If \c OS is an instance of \c raw_fd_ostream, this field will be // true. Otherwise, \c OS will be an raw_string_ostream. bool IsFDOStream; raw_ostream &OS; support::endian::Writer LE; }; } // end namespace llvm void CodeGenDataWriter::addRecord(OutlinedHashTreeRecord &Record) { assert(Record.HashTree && "empty hash tree in the record"); HashTreeRecord.HashTree = std::move(Record.HashTree); DataKind |= CGDataKind::FunctionOutlinedHashTree; } 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(DataKind & CGDataKind::FunctionOutlinedHashTree)) Header.DataKind |= static_cast(CGDataKind::FunctionOutlinedHashTree); Header.OutlinedHashTreeOffset = 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); return Error::success(); } Error CodeGenDataWriter::writeImpl(CGDataOStream &COS) { if (Error E = writeHeader(COS)) return E; uint64_t OutlinedHashTreeFieldStart = COS.tell(); if (hasOutlinedHashTree()) HashTreeRecord.serialize(COS.OS); // Back patch the offsets. CGDataPatchItem PatchItems[] = { {OutlinedHashTreeOffset, &OutlinedHashTreeFieldStart, 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"; // 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); // TODO: Write more yaml cgdata in order return Error::success(); }