Skip to content

Commit 7137021

Browse files
authored
[CGData] Make an option to skip reading Names into StableFunctionMap (#142095)
Names are used for debugging purpose and have no impact on codegen. For a non-trivial project, reading them consumes a lot of memory and slows down the compilation significantly. This patch adds a field in the indexed CGData to remember the total size of Names, and creates a command-line option to skip reading Names by advancing the pointer when deserializing the indexed CGData.
1 parent 3eb9b7d commit 7137021

18 files changed

+150
-57
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===- CGDataPatchItem.h ----------------------------------------*- C++ -*-===//
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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains support for patching codegen data.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CGDATA_CGDATAPATCHITEM_H
14+
#define LLVM_CGDATA_CGDATAPATCHITEM_H
15+
16+
#include "llvm/ADT/ArrayRef.h"
17+
18+
namespace llvm {
19+
20+
/// A struct to define how the data stream should be patched.
21+
struct CGDataPatchItem {
22+
// Where to patch.
23+
uint64_t Pos;
24+
// Source data.
25+
OwningArrayRef<uint64_t> D;
26+
27+
CGDataPatchItem(uint64_t Pos, const uint64_t *D, int N)
28+
: Pos(Pos), D(ArrayRef<uint64_t>(D, N)) {}
29+
};
30+
31+
} // namespace llvm
32+
33+
#endif // LLVM_CGDATA_CGDATAPATCHITEM_H

llvm/include/llvm/CGData/CodeGenData.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ enum CGDataVersion {
282282
Version1 = 1,
283283
// Version 2 supports the stable function merging map.
284284
Version2 = 2,
285+
// Version 3 adds the total size of the Names in the stable function map so
286+
// we can skip reading them into the memory for non-assertion builds.
287+
Version3 = 3,
285288
CurrentVersion = CG_DATA_INDEX_VERSION
286289
};
287290
const uint64_t Version = CGDataVersion::CurrentVersion;

llvm/include/llvm/CGData/CodeGenData.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,4 @@ CG_DATA_SECT_ENTRY(CG_merge, CG_DATA_QUOTE(CG_DATA_MERGE_COMMON),
4949
#endif
5050

5151
/* Indexed codegen data format version (start from 1). */
52-
#define CG_DATA_INDEX_VERSION 2
52+
#define CG_DATA_INDEX_VERSION 3

llvm/include/llvm/CGData/CodeGenDataWriter.h

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_CGDATA_CODEGENDATAWRITER_H
1414
#define LLVM_CGDATA_CODEGENDATAWRITER_H
1515

16+
#include "llvm/CGData/CGDataPatchItem.h"
1617
#include "llvm/CGData/CodeGenData.h"
1718
#include "llvm/CGData/OutlinedHashTreeRecord.h"
1819
#include "llvm/CGData/StableFunctionMapRecord.h"
@@ -22,21 +23,23 @@
2223

2324
namespace llvm {
2425

25-
/// A struct to define how the data stream should be patched.
26-
struct CGDataPatchItem {
27-
uint64_t Pos; // Where to patch.
28-
uint64_t *D; // Pointer to an array of source data.
29-
int N; // Number of elements in \c D array.
30-
};
31-
3226
/// A wrapper class to abstract writer stream with support of bytes
3327
/// back patching.
3428
class CGDataOStream {
29+
enum class OStreamKind {
30+
fd,
31+
string,
32+
svector,
33+
};
34+
3535
public:
3636
CGDataOStream(raw_fd_ostream &FD)
37-
: IsFDOStream(true), OS(FD), LE(FD, llvm::endianness::little) {}
37+
: Kind(OStreamKind::fd), OS(FD), LE(FD, llvm::endianness::little) {}
3838
CGDataOStream(raw_string_ostream &STR)
39-
: IsFDOStream(false), OS(STR), LE(STR, llvm::endianness::little) {}
39+
: Kind(OStreamKind::string), OS(STR), LE(STR, llvm::endianness::little) {}
40+
CGDataOStream(raw_svector_ostream &SVEC)
41+
: Kind(OStreamKind::svector), OS(SVEC),
42+
LE(SVEC, llvm::endianness::little) {}
4043

4144
uint64_t tell() { return OS.tell(); }
4245
void write(uint64_t V) { LE.write<uint64_t>(V); }
@@ -48,9 +51,7 @@ class CGDataOStream {
4851
// directly and it won't be reflected in the stream's internal buffer.
4952
LLVM_ABI void patch(ArrayRef<CGDataPatchItem> P);
5053

51-
// If \c OS is an instance of \c raw_fd_ostream, this field will be
52-
// true. Otherwise, \c OS will be an raw_string_ostream.
53-
bool IsFDOStream;
54+
OStreamKind Kind;
5455
raw_ostream &OS;
5556
support::endian::Writer LE;
5657
};

llvm/include/llvm/CGData/StableFunctionMapRecord.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#ifndef LLVM_CGDATA_STABLEFUNCTIONMAPRECORD_H
1717
#define LLVM_CGDATA_STABLEFUNCTIONMAPRECORD_H
1818

19+
#include "llvm/CGData/CGDataPatchItem.h"
1920
#include "llvm/CGData/StableFunctionMap.h"
2021
#include "llvm/ObjectYAML/YAML.h"
2122
#include "llvm/Support/Compiler.h"
@@ -36,13 +37,16 @@ struct StableFunctionMapRecord {
3637
/// A static helper function to serialize the stable function map without
3738
/// owning the stable function map.
3839
LLVM_ABI static void serialize(raw_ostream &OS,
39-
const StableFunctionMap *FunctionMap);
40+
const StableFunctionMap *FunctionMap,
41+
std::vector<CGDataPatchItem> &PatchItems);
4042

4143
/// Serialize the stable function map to a raw_ostream.
42-
LLVM_ABI void serialize(raw_ostream &OS) const;
44+
LLVM_ABI void serialize(raw_ostream &OS,
45+
std::vector<CGDataPatchItem> &PatchItems) const;
4346

4447
/// Deserialize the stable function map from a raw_ostream.
45-
LLVM_ABI void deserialize(const unsigned char *&Ptr);
48+
LLVM_ABI void deserialize(const unsigned char *&Ptr,
49+
bool ReadStableFunctionMapNames = true);
4650

4751
/// Serialize the stable function map to a YAML stream.
4852
LLVM_ABI void serializeYAML(yaml::Output &YOS) const;

llvm/lib/CGData/CodeGenData.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ Expected<Header> Header::readFromBuffer(const unsigned char *Curr) {
186186
return make_error<CGDataError>(cgdata_error::unsupported_version);
187187
H.DataKind = endian::readNext<uint32_t, endianness::little, unaligned>(Curr);
188188

189-
static_assert(IndexedCGData::CGDataVersion::CurrentVersion == Version2,
189+
static_assert(IndexedCGData::CGDataVersion::CurrentVersion == Version3,
190190
"Please update the offset computation below if a new field has "
191191
"been added to the header.");
192192
H.OutlinedHashTreeOffset =

llvm/lib/CGData/CodeGenDataReader.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@
1313
#include "llvm/CGData/CodeGenDataReader.h"
1414
#include "llvm/CGData/OutlinedHashTreeRecord.h"
1515
#include "llvm/Object/ObjectFile.h"
16+
#include "llvm/Support/CommandLine.h"
1617
#include "llvm/Support/MemoryBuffer.h"
1718

1819
#define DEBUG_TYPE "cg-data-reader"
1920

2021
using namespace llvm;
2122

23+
static 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+
2229
namespace llvm {
2330

2431
static Expected<std::unique_ptr<MemoryBuffer>>
@@ -106,7 +113,7 @@ Error IndexedCodeGenDataReader::read() {
106113
const unsigned char *Ptr = Start + Header.StableFunctionMapOffset;
107114
if (Ptr >= End)
108115
return error(cgdata_error::eof);
109-
FunctionMapRecord.deserialize(Ptr);
116+
FunctionMapRecord.deserialize(Ptr, IndexedCodeGenDataReadFunctionMapNames);
110117
}
111118

112119
return success();

llvm/lib/CGData/CodeGenDataWriter.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,46 @@ using namespace llvm;
1919
void CGDataOStream::patch(ArrayRef<CGDataPatchItem> P) {
2020
using namespace support;
2121

22-
if (IsFDOStream) {
22+
switch (Kind) {
23+
case OStreamKind::fd: {
2324
raw_fd_ostream &FDOStream = static_cast<raw_fd_ostream &>(OS);
2425
const uint64_t LastPos = FDOStream.tell();
2526
for (const auto &K : P) {
2627
FDOStream.seek(K.Pos);
27-
for (int I = 0; I < K.N; I++)
28+
for (size_t I = 0; I < K.D.size(); ++I)
2829
write(K.D[I]);
2930
}
3031
// Reset the stream to the last position after patching so that users
3132
// don't accidentally overwrite data. This makes it consistent with
3233
// the string stream below which replaces the data directly.
3334
FDOStream.seek(LastPos);
34-
} else {
35+
break;
36+
}
37+
case OStreamKind::string: {
3538
raw_string_ostream &SOStream = static_cast<raw_string_ostream &>(OS);
3639
std::string &Data = SOStream.str(); // with flush
3740
for (const auto &K : P) {
38-
for (int I = 0; I < K.N; I++) {
41+
for (size_t I = 0; I < K.D.size(); ++I) {
3942
uint64_t Bytes =
4043
endian::byte_swap<uint64_t, llvm::endianness::little>(K.D[I]);
4144
Data.replace(K.Pos + I * sizeof(uint64_t), sizeof(uint64_t),
4245
reinterpret_cast<const char *>(&Bytes), sizeof(uint64_t));
4346
}
4447
}
48+
break;
49+
}
50+
case OStreamKind::svector: {
51+
raw_svector_ostream &VOStream = static_cast<raw_svector_ostream &>(OS);
52+
for (const auto &K : P) {
53+
for (size_t I = 0; I < K.D.size(); ++I) {
54+
uint64_t Bytes =
55+
endian::byte_swap<uint64_t, llvm::endianness::little>(K.D[I]);
56+
VOStream.pwrite(reinterpret_cast<const char *>(&Bytes),
57+
sizeof(uint64_t), K.Pos + I * sizeof(uint64_t));
58+
}
59+
}
60+
break;
61+
}
4562
}
4663
}
4764

@@ -106,17 +123,20 @@ Error CodeGenDataWriter::writeImpl(CGDataOStream &COS) {
106123
if (Error E = writeHeader(COS))
107124
return E;
108125

126+
std::vector<CGDataPatchItem> PatchItems;
127+
109128
uint64_t OutlinedHashTreeFieldStart = COS.tell();
110129
if (hasOutlinedHashTree())
111130
HashTreeRecord.serialize(COS.OS);
112131
uint64_t StableFunctionMapFieldStart = COS.tell();
113132
if (hasStableFunctionMap())
114-
FunctionMapRecord.serialize(COS.OS);
133+
FunctionMapRecord.serialize(COS.OS, PatchItems);
115134

116135
// Back patch the offsets.
117-
CGDataPatchItem PatchItems[] = {
118-
{OutlinedHashTreeOffset, &OutlinedHashTreeFieldStart, 1},
119-
{StableFunctionMapOffset, &StableFunctionMapFieldStart, 1}};
136+
PatchItems.emplace_back(OutlinedHashTreeOffset, &OutlinedHashTreeFieldStart,
137+
1);
138+
PatchItems.emplace_back(StableFunctionMapOffset, &StableFunctionMapFieldStart,
139+
1);
120140
COS.patch(PatchItems);
121141

122142
return Error::success();

llvm/lib/CGData/StableFunctionMapRecord.cpp

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,26 +77,32 @@ static IndexOperandHashVecType getStableIndexOperandHashes(
7777
return IndexOperandHashes;
7878
}
7979

80-
void StableFunctionMapRecord::serialize(raw_ostream &OS) const {
81-
serialize(OS, FunctionMap.get());
80+
void StableFunctionMapRecord::serialize(
81+
raw_ostream &OS, std::vector<CGDataPatchItem> &PatchItems) const {
82+
serialize(OS, FunctionMap.get(), PatchItems);
8283
}
8384

84-
void StableFunctionMapRecord::serialize(raw_ostream &OS,
85-
const StableFunctionMap *FunctionMap) {
85+
void StableFunctionMapRecord::serialize(
86+
raw_ostream &OS, const StableFunctionMap *FunctionMap,
87+
std::vector<CGDataPatchItem> &PatchItems) {
8688
support::endian::Writer Writer(OS, endianness::little);
8789

8890
// Write Names.
8991
ArrayRef<std::string> Names = FunctionMap->getNames();
90-
uint32_t ByteSize = 4;
9192
Writer.write<uint32_t>(Names.size());
92-
for (auto &Name : Names) {
93+
// Remember the position, write back the total size of Names, so we can skip
94+
// reading them if needed.
95+
const uint64_t NamesByteSizeOffset = Writer.OS.tell();
96+
Writer.write<uint64_t>(0);
97+
for (auto &Name : Names)
9398
Writer.OS << Name << '\0';
94-
ByteSize += Name.size() + 1;
95-
}
96-
// Align ByteSize to 4 bytes.
97-
uint32_t Padding = offsetToAlignment(ByteSize, Align(4));
99+
// Align current position to 4 bytes.
100+
uint32_t Padding = offsetToAlignment(Writer.OS.tell(), Align(4));
98101
for (uint32_t I = 0; I < Padding; ++I)
99102
Writer.OS << '\0';
103+
const auto NamesByteSize =
104+
Writer.OS.tell() - NamesByteSizeOffset - sizeof(NamesByteSizeOffset);
105+
PatchItems.emplace_back(NamesByteSizeOffset, &NamesByteSize, 1);
100106

101107
// Write StableFunctionEntries whose pointers are sorted.
102108
auto FuncEntries = getStableFunctionEntries(*FunctionMap);
@@ -120,7 +126,8 @@ void StableFunctionMapRecord::serialize(raw_ostream &OS,
120126
}
121127
}
122128

123-
void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr) {
129+
void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr,
130+
bool ReadStableFunctionMapNames) {
124131
// Assert that Ptr is 4-byte aligned
125132
assert(((uintptr_t)Ptr % 4) == 0);
126133
// Read Names.
@@ -129,13 +136,23 @@ void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr) {
129136
// Early exit if there is no name.
130137
if (NumNames == 0)
131138
return;
132-
for (unsigned I = 0; I < NumNames; ++I) {
133-
StringRef Name(reinterpret_cast<const char *>(Ptr));
134-
Ptr += Name.size() + 1;
135-
FunctionMap->getIdOrCreateForName(Name);
139+
const auto NamesByteSize =
140+
endian::readNext<uint64_t, endianness::little, unaligned>(Ptr);
141+
const auto NamesOffset = reinterpret_cast<uintptr_t>(Ptr);
142+
if (ReadStableFunctionMapNames) {
143+
for (unsigned I = 0; I < NumNames; ++I) {
144+
StringRef Name(reinterpret_cast<const char *>(Ptr));
145+
Ptr += Name.size() + 1;
146+
FunctionMap->getIdOrCreateForName(Name);
147+
}
148+
// Align Ptr to 4 bytes.
149+
Ptr = reinterpret_cast<const uint8_t *>(alignAddr(Ptr, Align(4)));
150+
assert(reinterpret_cast<uintptr_t>(Ptr) - NamesOffset == NamesByteSize &&
151+
"NamesByteSize does not match the actual size of names");
152+
} else {
153+
// skip reading Names by advancing the pointer.
154+
Ptr = reinterpret_cast<const uint8_t *>(NamesOffset + NamesByteSize);
136155
}
137-
// Align Ptr to 4 bytes.
138-
Ptr = reinterpret_cast<const uint8_t *>(alignAddr(Ptr, Align(4)));
139156

140157
// Read StableFunctionEntries.
141158
auto NumFuncs =

llvm/lib/CodeGen/GlobalMergeFunctions.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/ADT/Statistic.h"
1515
#include "llvm/Analysis/ModuleSummaryAnalysis.h"
1616
#include "llvm/CGData/CodeGenData.h"
17+
#include "llvm/CGData/CodeGenDataWriter.h"
1718
#include "llvm/IR/IRBuilder.h"
1819
#include "llvm/IR/StructuralHash.h"
1920
#include "llvm/InitializePasses.h"
@@ -526,7 +527,10 @@ void GlobalMergeFunc::emitFunctionMap(Module &M) {
526527
SmallVector<char> Buf;
527528
raw_svector_ostream OS(Buf);
528529

529-
StableFunctionMapRecord::serialize(OS, LocalFunctionMap.get());
530+
std::vector<CGDataPatchItem> PatchItems;
531+
StableFunctionMapRecord::serialize(OS, LocalFunctionMap.get(), PatchItems);
532+
CGDataOStream COS(OS);
533+
COS.patch(PatchItems);
530534

531535
std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(
532536
OS.str(), "in-memory stable function map", false);

llvm/test/tools/llvm-cgdata/empty.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN: llvm-cgdata --show %t_emptyheader.cgdata | count 0
1616

1717
# The version number appears when asked, as it's in the header
1818
RUN: llvm-cgdata --show --cgdata-version %t_emptyheader.cgdata | FileCheck %s --check-prefix=VERSION
19-
VERSION: Version: 2
19+
VERSION: Version: 3
2020

2121
# When converting a binary file (w/ the header only) to a text file, it's an empty file as the text format does not have an explicit header.
2222
RUN: llvm-cgdata --convert %t_emptyheader.cgdata --format text | count 0
@@ -30,7 +30,7 @@ RUN: llvm-cgdata --convert %t_emptyheader.cgdata --format text | count 0
3030
# uint64_t StableFunctionMapOffset;
3131
# }
3232
RUN: printf '\xffcgdata\x81' > %t_header.cgdata
33-
RUN: printf '\x02\x00\x00\x00' >> %t_header.cgdata
33+
RUN: printf '\x03\x00\x00\x00' >> %t_header.cgdata
3434
RUN: printf '\x00\x00\x00\x00' >> %t_header.cgdata
3535
RUN: printf '\x20\x00\x00\x00\x00\x00\x00\x00' >> %t_header.cgdata
3636
RUN: printf '\x20\x00\x00\x00\x00\x00\x00\x00' >> %t_header.cgdata

llvm/test/tools/llvm-cgdata/error.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ RUN: printf '\xffcgdata\x81' > %t_corrupt.cgdata
2222
RUN: not llvm-cgdata --show %t_corrupt.cgdata 2>&1 | FileCheck %s --check-prefix=CORRUPT
2323
CORRUPT: {{.}}cgdata: invalid codegen data (file header is corrupt)
2424

25-
# The current version 2 while the header says 3.
25+
# The current version 3 while the header says 4.
2626
RUN: printf '\xffcgdata\x81' > %t_version.cgdata
27-
RUN: printf '\x03\x00\x00\x00' >> %t_version.cgdata
27+
RUN: printf '\x04\x00\x00\x00' >> %t_version.cgdata
2828
RUN: printf '\x00\x00\x00\x00' >> %t_version.cgdata
2929
RUN: printf '\x20\x00\x00\x00\x00\x00\x00\x00' >> %t_version.cgdata
3030
RUN: printf '\x20\x00\x00\x00\x00\x00\x00\x00' >> %t_version.cgdata

llvm/test/tools/llvm-cgdata/merge-combined-funcmap-hashtree.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@ CHECK-NEXT: Mergeable function Count: 0
6363

6464
;--- merge-both-template.ll
6565
@.data1 = private unnamed_addr constant [72 x i8] c"<RAW_1_BYTES>", section "__DATA,__llvm_outline"
66-
@.data2 = private unnamed_addr constant [60 x i8] c"<RAW_2_BYTES>", section "__DATA,__llvm_merge"
66+
@.data2 = private unnamed_addr constant [68 x i8] c"<RAW_2_BYTES>", section "__DATA,__llvm_merge"

llvm/test/tools/llvm-cgdata/merge-funcmap-archive.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ MAP-NEXT: ...
6565
...
6666

6767
;--- merge-1-template.ll
68-
@.data = private unnamed_addr constant [60 x i8] c"<RAW_1_BYTES>", section "__DATA,__llvm_merge"
68+
@.data = private unnamed_addr constant [68 x i8] c"<RAW_1_BYTES>", section "__DATA,__llvm_merge"
6969

7070
;--- raw-2.cgtext
7171
:stable_function_map
@@ -80,4 +80,4 @@ MAP-NEXT: ...
8080
...
8181

8282
;--- merge-2-template.ll
83-
@.data = private unnamed_addr constant [60 x i8] c"<RAW_2_BYTES>", section "__DATA,__llvm_merge"
83+
@.data = private unnamed_addr constant [68 x i8] c"<RAW_2_BYTES>", section "__DATA,__llvm_merge"

0 commit comments

Comments
 (0)