Skip to content

Commit bd6531b

Browse files
[LTO] Introduce a new class ImportIDTable (#106503)
The new class implements a deduplication table to convert import list elements: {SourceModule, GUID, Definition/Declaration} into 32-bit integers, and vice versa. This patch adds a unit test but does not add a use yet. To be precise, the deduplication table holds {SourceModule, GUID} pairs. We use the bottom one bit of the 32-bit integers to indicate whether we have a definition or declaration. A subsequent patch will collapse the import list hierarchy -- FunctionsToImportTy holding many instances of FunctionsToImportTy -- down to DenseSet<uint32_t> with each element indexing into the deduplication table above. This will address multiple sources of space inefficiency.
1 parent a777a93 commit bd6531b

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

llvm/include/llvm/Transforms/IPO/FunctionImport.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_TRANSFORMS_IPO_FUNCTIONIMPORT_H
1111

1212
#include "llvm/ADT/DenseSet.h"
13+
#include "llvm/ADT/MapVector.h"
1314
#include "llvm/ADT/StringRef.h"
1415
#include "llvm/IR/GlobalValue.h"
1516
#include "llvm/IR/ModuleSummaryIndex.h"
@@ -96,6 +97,76 @@ class FunctionImporter {
9697
std::tuple<unsigned, const GlobalValueSummary *,
9798
std::unique_ptr<ImportFailureInfo>>>;
9899

100+
// Issues import IDs. Each ID uniquely corresponds to a tuple of
101+
// (FromModule, GUID, Definition/Declaration).
102+
//
103+
// The import IDs make the import list space efficient by referring to each
104+
// import with a 32-bit integer ID while maintaining a central table that maps
105+
// those integer IDs to tuples of (FromModule, GUID, Def/Decl).
106+
//
107+
// In one large application, a pair of (FromModule, GUID) is mentioned in
108+
// import lists more than 50 times on average across all destination modules.
109+
// Mentioning the 32-byte tuple:
110+
//
111+
// std::tuple<StringRef, GlobalValue::GUID, GlobalValueSummary::ImportKind>
112+
//
113+
// 50 times by value in various import lists would be costly. We can reduce
114+
// the memory footprint of import lists by placing one copy in a central table
115+
// and referring to it with 32-bit integer IDs.
116+
//
117+
// To save space within the central table, we only store pairs of
118+
// (FromModule, GUID) in the central table. In the actual 32-bit integer ID,
119+
// the top 31 bits index into the central table while the bottom 1 bit
120+
// indicates whether an ID is for GlobalValueSummary::Declaration or
121+
// GlobalValueSummary::Definition.
122+
class ImportIDTable {
123+
public:
124+
using ImportIDTy = uint32_t;
125+
126+
// Create a pair of import IDs [Def, Decl] for a given pair of FromModule
127+
// and GUID.
128+
std::pair<ImportIDTy, ImportIDTy> createImportIDs(StringRef FromModule,
129+
GlobalValue::GUID GUID) {
130+
auto Key = std::make_pair(FromModule, GUID);
131+
auto InsertResult = TheTable.try_emplace(Key, TheTable.size());
132+
return makeIDPair(InsertResult.first->second);
133+
}
134+
135+
// Get a pair of previously created import IDs [Def, Decl] for a given pair
136+
// of FromModule and GUID. Returns std::nullopt if not available.
137+
std::optional<std::pair<ImportIDTy, ImportIDTy>>
138+
getImportIDs(StringRef FromModule, GlobalValue::GUID GUID) {
139+
auto Key = std::make_pair(FromModule, GUID);
140+
auto It = TheTable.find(Key);
141+
if (It != TheTable.end())
142+
return makeIDPair(It->second);
143+
return std::nullopt;
144+
}
145+
146+
// Return a tuple of [FromModule, GUID, Def/Decl] that a given ImportID
147+
// corresponds to.
148+
std::tuple<StringRef, GlobalValue::GUID, GlobalValueSummary::ImportKind>
149+
lookup(ImportIDTy ImportID) const {
150+
GlobalValueSummary::ImportKind Kind =
151+
(ImportID & 1) ? GlobalValueSummary::Declaration
152+
: GlobalValueSummary::Definition;
153+
auto It = TheTable.begin() + (ImportID >> 1);
154+
StringRef FromModule = It->first.first;
155+
GlobalValue::GUID GUID = It->first.second;
156+
return std::make_tuple(FromModule, GUID, Kind);
157+
}
158+
159+
private:
160+
// Make a pair of import IDs [Def, Decl] from an index into TheTable.
161+
static std::pair<ImportIDTy, ImportIDTy> makeIDPair(ImportIDTy Index) {
162+
ImportIDTy Def = Index << 1;
163+
ImportIDTy Decl = Def | 1;
164+
return std::make_pair(Def, Decl);
165+
}
166+
167+
MapVector<std::pair<StringRef, GlobalValue::GUID>, ImportIDTy> TheTable;
168+
};
169+
99170
/// The map maintains the list of imports. Conceptually, it is a collection
100171
/// of tuples of the form:
101172
///

llvm/unittests/Transforms/IPO/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ add_llvm_unittest(IPOTests
1313
WholeProgramDevirt.cpp
1414
AttributorTest.cpp
1515
FunctionSpecializationTest.cpp
16+
ImportIDTableTests.cpp
1617
)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===- ImportIDTableTests.cpp - Unit tests for ImportIDTable --------------===//
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+
#include "llvm/Transforms/IPO/FunctionImport.h"
10+
#include "gmock/gmock.h"
11+
#include "gtest/gtest.h"
12+
#include <set>
13+
#include <type_traits>
14+
15+
using namespace llvm;
16+
17+
TEST(ImportIDTableTests, Basic) {
18+
FunctionImporter::ImportIDTable Table;
19+
20+
auto [Def, Decl] = Table.createImportIDs("mod", 123U);
21+
auto [Def2, Decl2] = Table.createImportIDs("stuff", 456U);
22+
23+
// Def and Decl must be of the same unsigned integer type.
24+
static_assert(
25+
std::is_unsigned_v<FunctionImporter::ImportIDTable::ImportIDTy>);
26+
static_assert(std::is_same_v<FunctionImporter::ImportIDTable::ImportIDTy,
27+
decltype(Def)>);
28+
static_assert(std::is_same_v<FunctionImporter::ImportIDTable::ImportIDTy,
29+
decltype(Decl)>);
30+
31+
// Check that all IDs are unique.
32+
std::set<FunctionImporter::ImportIDTable::ImportIDTy> IDs = {Def, Decl, Def2,
33+
Decl2};
34+
EXPECT_THAT(IDs, ::testing::SizeIs(4));
35+
36+
// Verify what Def maps to.
37+
auto DefTuple = Table.lookup(Def);
38+
EXPECT_EQ(std::get<0>(DefTuple), StringRef("mod"));
39+
EXPECT_EQ(std::get<1>(DefTuple), 123U);
40+
EXPECT_EQ(std::get<2>(DefTuple), GlobalValueSummary::Definition);
41+
42+
// Verify what Decl maps to.
43+
auto DeclTuple = Table.lookup(Decl);
44+
EXPECT_EQ(std::get<0>(DeclTuple), StringRef("mod"));
45+
EXPECT_EQ(std::get<1>(DeclTuple), 123U);
46+
EXPECT_EQ(std::get<2>(DeclTuple), GlobalValueSummary::Declaration);
47+
48+
// Verify what Def2 maps to.
49+
auto Def2Tuple = Table.lookup(Def2);
50+
EXPECT_EQ(std::get<0>(Def2Tuple), StringRef("stuff"));
51+
EXPECT_EQ(std::get<1>(Def2Tuple), 456U);
52+
EXPECT_EQ(std::get<2>(Def2Tuple), GlobalValueSummary::Definition);
53+
54+
// Verify what Decl2 maps to.
55+
auto Decl2Tuple = Table.lookup(Decl2);
56+
EXPECT_EQ(std::get<0>(Decl2Tuple), StringRef("stuff"));
57+
EXPECT_EQ(std::get<1>(Decl2Tuple), 456U);
58+
EXPECT_EQ(std::get<2>(Decl2Tuple), GlobalValueSummary::Declaration);
59+
}
60+
61+
TEST(ImportIDTableTests, Duplicates) {
62+
FunctionImporter::ImportIDTable Table;
63+
64+
auto [Def1, Decl1] = Table.createImportIDs("mod", 123U);
65+
auto [Def2, Decl2] = Table.createImportIDs("mod", 123U);
66+
67+
// Verify we get the same IDs back.
68+
EXPECT_EQ(Def1, Def2);
69+
EXPECT_EQ(Decl1, Decl2);
70+
}
71+
72+
TEST(ImportIDTableTests, Present) {
73+
FunctionImporter::ImportIDTable Table;
74+
75+
auto [Def, Decl] = Table.createImportIDs("mod", 123U);
76+
auto Result = Table.getImportIDs("mod", 123U);
77+
78+
// Verify that we get the same IDs back.
79+
ASSERT_NE(Result, std::nullopt);
80+
EXPECT_EQ(Result->first, Def);
81+
EXPECT_EQ(Result->second, Decl);
82+
}
83+
84+
TEST(ImportIDTableTests, Missing) {
85+
FunctionImporter::ImportIDTable Table;
86+
87+
auto Result = Table.getImportIDs("mod", 123U);
88+
89+
// Verify that we get std::nullopt for a non-existent pair.
90+
EXPECT_EQ(Result, std::nullopt);
91+
}

0 commit comments

Comments
 (0)