The llvm-cgdata tool has been introduced to handle reading and writing of codegen data. This data includes an optimistic codegen summary that can be utilized to enhance subsequent codegen. Currently, the tool supports saving and restoring the outlined hash tree, facilitating machine function outlining across modules. Additional codegen summaries can be incorporated into separate sections as required. This patch primarily establishes basic support for the reader and writer, similar to llvm-profdata. The high-level operations of llvm-cgdata are as follows: 1. It reads local raw codegen data from a custom section (for example, __llvm_outline) embedded in native binary files 2. It merges local raw codegen data into an indexed codegen data, complete with a suitable header. 3. It handles reading and writing of the indexed codegen data into a standalone file. This depends on https://github.com/llvm/llvm-project/pull/89792. This is a patch for https://discourse.llvm.org/t/rfc-enhanced-machine-outliner-part-2-thinlto-nolto/78753. --------- Co-authored-by: Kyungwoo Lee <kyulee@fb.com>
163 lines
5.1 KiB
C++
163 lines
5.1 KiB
C++
//===- 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<uint64_t>(V); }
|
|
void write32(uint32_t V) { LE.write<uint32_t>(V); }
|
|
void write8(uint8_t V) { LE.write<uint8_t>(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<CGDataPatchItem> P) {
|
|
using namespace support;
|
|
|
|
if (IsFDOStream) {
|
|
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 (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<raw_string_ostream &>(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<uint64_t, llvm::endianness::little>(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<bool>(DataKind & CGDataKind::FunctionOutlinedHashTree))
|
|
Header.DataKind |=
|
|
static_cast<uint32_t>(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();
|
|
}
|