Skip to content

Commit 2ccf7ed

Browse files
jaredwylhames
andauthored
[JITLink] Switch to SymbolStringPtr for Symbol names (#115796)
Use SymbolStringPtr for Symbol names in LinkGraph. This reduces string interning on the boundary between JITLink and ORC, and allows pointer comparisons (rather than string comparisons) between Symbol names. This should improve the performance and readability of code that bridges between JITLink and ORC (e.g. ObjectLinkingLayer and ObjectLinkingLayer::Plugins). To enable use of SymbolStringPtr a std::shared_ptr<SymbolStringPool> is added to LinkGraph and threaded through to its construction sites in LLVM and Bolt. All LinkGraphs that are to have symbol names compared by pointer equality must point to the same SymbolStringPool instance, which in ORC sessions should be the pool attached to the ExecutionSession. --------- Co-authored-by: Lang Hames <[email protected]>
1 parent e6cf5d2 commit 2ccf7ed

File tree

81 files changed

+667
-494
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+667
-494
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ autoconf/autom4te.cache
5959
# VS2017 and VSCode config files.
6060
.vscode
6161
.vs
62+
#zed config files
63+
.zed
6264
# pythonenv for github Codespaces
6365
pythonenv*
6466
# clangd index. (".clangd" is a config file now, thus trailing slash)

bolt/include/bolt/Core/BinaryContext.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "llvm/ADT/iterator.h"
2929
#include "llvm/BinaryFormat/Dwarf.h"
3030
#include "llvm/BinaryFormat/MachO.h"
31+
#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
3132
#include "llvm/MC/MCAsmInfo.h"
3233
#include "llvm/MC/MCCodeEmitter.h"
3334
#include "llvm/MC/MCContext.h"
@@ -276,11 +277,10 @@ class BinaryContext {
276277
void deregisterSectionName(const BinarySection &Section);
277278

278279
public:
279-
static Expected<std::unique_ptr<BinaryContext>>
280-
createBinaryContext(Triple TheTriple, StringRef InputFileName,
281-
SubtargetFeatures *Features, bool IsPIC,
282-
std::unique_ptr<DWARFContext> DwCtx,
283-
JournalingStreams Logger);
280+
static Expected<std::unique_ptr<BinaryContext>> createBinaryContext(
281+
Triple TheTriple, std::shared_ptr<orc::SymbolStringPool> SSP,
282+
StringRef InputFileName, SubtargetFeatures *Features, bool IsPIC,
283+
std::unique_ptr<DWARFContext> DwCtx, JournalingStreams Logger);
284284

285285
/// Superset of compiler units that will contain overwritten code that needs
286286
/// new debug info. In a few cases, functions may end up not being
@@ -372,6 +372,7 @@ class BinaryContext {
372372
bool hasSymbolsWithFileName() const { return HasSymbolsWithFileName; }
373373
void setHasSymbolsWithFileName(bool Value) { HasSymbolsWithFileName = Value; }
374374

375+
std::shared_ptr<orc::SymbolStringPool> getSymbolStringPool() { return SSP; }
375376
/// Return true if relocations against symbol with a given name
376377
/// must be created.
377378
bool forceSymbolRelocations(StringRef SymbolName) const;
@@ -631,6 +632,8 @@ class BinaryContext {
631632

632633
std::unique_ptr<Triple> TheTriple;
633634

635+
std::shared_ptr<orc::SymbolStringPool> SSP;
636+
634637
const Target *TheTarget;
635638

636639
std::string TripleName;
@@ -807,8 +810,10 @@ class BinaryContext {
807810

808811
BinaryContext(std::unique_ptr<MCContext> Ctx,
809812
std::unique_ptr<DWARFContext> DwCtx,
810-
std::unique_ptr<Triple> TheTriple, const Target *TheTarget,
811-
std::string TripleName, std::unique_ptr<MCCodeEmitter> MCE,
813+
std::unique_ptr<Triple> TheTriple,
814+
std::shared_ptr<orc::SymbolStringPool> SSP,
815+
const Target *TheTarget, std::string TripleName,
816+
std::unique_ptr<MCCodeEmitter> MCE,
812817
std::unique_ptr<MCObjectFileInfo> MOFI,
813818
std::unique_ptr<const MCAsmInfo> AsmInfo,
814819
std::unique_ptr<const MCInstrInfo> MII,

bolt/lib/Core/BinaryContext.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ void BinaryContext::logBOLTErrorsAndQuitOnFatal(Error E) {
123123
BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
124124
std::unique_ptr<DWARFContext> DwCtx,
125125
std::unique_ptr<Triple> TheTriple,
126+
std::shared_ptr<orc::SymbolStringPool> SSP,
126127
const Target *TheTarget, std::string TripleName,
127128
std::unique_ptr<MCCodeEmitter> MCE,
128129
std::unique_ptr<MCObjectFileInfo> MOFI,
@@ -136,12 +137,12 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
136137
std::unique_ptr<MCDisassembler> DisAsm,
137138
JournalingStreams Logger)
138139
: Ctx(std::move(Ctx)), DwCtx(std::move(DwCtx)),
139-
TheTriple(std::move(TheTriple)), TheTarget(TheTarget),
140-
TripleName(TripleName), MCE(std::move(MCE)), MOFI(std::move(MOFI)),
141-
AsmInfo(std::move(AsmInfo)), MII(std::move(MII)), STI(std::move(STI)),
142-
InstPrinter(std::move(InstPrinter)), MIA(std::move(MIA)),
143-
MIB(std::move(MIB)), MRI(std::move(MRI)), DisAsm(std::move(DisAsm)),
144-
Logger(Logger), InitialDynoStats(isAArch64()) {
140+
TheTriple(std::move(TheTriple)), SSP(std::move(SSP)),
141+
TheTarget(TheTarget), TripleName(TripleName), MCE(std::move(MCE)),
142+
MOFI(std::move(MOFI)), AsmInfo(std::move(AsmInfo)), MII(std::move(MII)),
143+
STI(std::move(STI)), InstPrinter(std::move(InstPrinter)),
144+
MIA(std::move(MIA)), MIB(std::move(MIB)), MRI(std::move(MRI)),
145+
DisAsm(std::move(DisAsm)), Logger(Logger), InitialDynoStats(isAArch64()) {
145146
RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
146147
PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
147148
}
@@ -159,8 +160,9 @@ BinaryContext::~BinaryContext() {
159160
/// Create BinaryContext for a given architecture \p ArchName and
160161
/// triple \p TripleName.
161162
Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
162-
Triple TheTriple, StringRef InputFileName, SubtargetFeatures *Features,
163-
bool IsPIC, std::unique_ptr<DWARFContext> DwCtx, JournalingStreams Logger) {
163+
Triple TheTriple, std::shared_ptr<orc::SymbolStringPool> SSP,
164+
StringRef InputFileName, SubtargetFeatures *Features, bool IsPIC,
165+
std::unique_ptr<DWARFContext> DwCtx, JournalingStreams Logger) {
164166
StringRef ArchName = "";
165167
std::string FeaturesStr = "";
166168
switch (TheTriple.getArch()) {
@@ -283,8 +285,8 @@ Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
283285

284286
auto BC = std::make_unique<BinaryContext>(
285287
std::move(Ctx), std::move(DwCtx), std::make_unique<Triple>(TheTriple),
286-
TheTarget, std::string(TripleName), std::move(MCE), std::move(MOFI),
287-
std::move(AsmInfo), std::move(MII), std::move(STI),
288+
std::move(SSP), TheTarget, std::string(TripleName), std::move(MCE),
289+
std::move(MOFI), std::move(AsmInfo), std::move(MII), std::move(STI),
288290
std::move(InstructionPrinter), std::move(MIA), nullptr, std::move(MRI),
289291
std::move(DisAsm), Logger);
290292

bolt/lib/Rewrite/DWARFRewriter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1691,7 +1691,8 @@ namespace {
16911691
std::unique_ptr<BinaryContext>
16921692
createDwarfOnlyBC(const object::ObjectFile &File) {
16931693
return cantFail(BinaryContext::createBinaryContext(
1694-
File.makeTriple(), File.getFileName(), nullptr, false,
1694+
File.makeTriple(), std::make_shared<orc::SymbolStringPool>(),
1695+
File.getFileName(), nullptr, false,
16951696
DWARFContext::create(File, DWARFContext::ProcessDebugRelocations::Ignore,
16961697
nullptr, "", WithColor::defaultErrorHandler,
16971698
WithColor::defaultWarningHandler),

bolt/lib/Rewrite/JITLinkLinker.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ struct JITLinkLinker::Context : jitlink::JITLinkContext {
122122
jitlink::AsyncLookupResult AllResults;
123123

124124
for (const auto &Symbol : Symbols) {
125-
std::string SymName = Symbol.first.str();
125+
std::string SymName = (*Symbol.first).str();
126126
LLVM_DEBUG(dbgs() << "BOLT: looking for " << SymName << "\n");
127127

128128
if (auto Address = Linker.lookupSymbol(SymName)) {
@@ -167,7 +167,9 @@ struct JITLinkLinker::Context : jitlink::JITLinkContext {
167167
Error notifyResolved(jitlink::LinkGraph &G) override {
168168
for (auto *Symbol : G.defined_symbols()) {
169169
SymbolInfo Info{Symbol->getAddress().getValue(), Symbol->getSize()};
170-
Linker.Symtab.insert({Symbol->getName().str(), Info});
170+
auto Name =
171+
Symbol->hasName() ? (*Symbol->getName()).str() : std::string();
172+
Linker.Symtab.insert({std::move(Name), Info});
171173
}
172174

173175
return Error::success();
@@ -189,7 +191,7 @@ JITLinkLinker::~JITLinkLinker() { cantFail(MM->deallocate(std::move(Allocs))); }
189191

190192
void JITLinkLinker::loadObject(MemoryBufferRef Obj,
191193
SectionsMapper MapSections) {
192-
auto LG = jitlink::createLinkGraphFromObject(Obj);
194+
auto LG = jitlink::createLinkGraphFromObject(Obj, BC.getSymbolStringPool());
193195
if (auto E = LG.takeError()) {
194196
errs() << "BOLT-ERROR: JITLink failed: " << E << '\n';
195197
exit(1);

bolt/lib/Rewrite/MachORewriteInstance.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ MachORewriteInstance::MachORewriteInstance(object::MachOObjectFile *InputFile,
7474
ErrorAsOutParameter EAO(&Err);
7575
Relocation::Arch = InputFile->makeTriple().getArch();
7676
auto BCOrErr = BinaryContext::createBinaryContext(
77-
InputFile->makeTriple(), InputFile->getFileName(), nullptr,
77+
InputFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
78+
InputFile->getFileName(), nullptr,
7879
/* IsPIC */ true, DWARFContext::create(*InputFile),
7980
{llvm::outs(), llvm::errs()});
8081
if (Error E = BCOrErr.takeError()) {

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,8 @@ RewriteInstance::RewriteInstance(ELFObjectFileBase *File, const int Argc,
356356

357357
Relocation::Arch = TheTriple.getArch();
358358
auto BCOrErr = BinaryContext::createBinaryContext(
359-
TheTriple, File->getFileName(), Features.get(), IsPIC,
359+
TheTriple, std::make_shared<orc::SymbolStringPool>(), File->getFileName(),
360+
Features.get(), IsPIC,
360361
DWARFContext::create(*File, DWARFContext::ProcessDebugRelocations::Ignore,
361362
nullptr, opts::DWPPathName,
362363
WithColor::defaultErrorHandler,

bolt/unittests/Core/BinaryContext.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
4848
void initializeBOLT() {
4949
Relocation::Arch = ObjFile->makeTriple().getArch();
5050
BC = cantFail(BinaryContext::createBinaryContext(
51-
ObjFile->makeTriple(), ObjFile->getFileName(), nullptr, true,
51+
ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
52+
ObjFile->getFileName(), nullptr, true,
5253
DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
5354
ASSERT_FALSE(!BC);
5455
}
@@ -216,4 +217,4 @@ TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {
216217
BC->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);
217218
ASSERT_TRUE(BaseAddress.has_value());
218219
ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);
219-
}
220+
}

bolt/unittests/Core/MCPlusBuilder.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
5858
void initializeBolt() {
5959
Relocation::Arch = ObjFile->makeTriple().getArch();
6060
BC = cantFail(BinaryContext::createBinaryContext(
61-
ObjFile->makeTriple(), ObjFile->getFileName(), nullptr, true,
61+
ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
62+
ObjFile->getFileName(), nullptr, true,
6263
DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
6364
ASSERT_FALSE(!BC);
6465
BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(

bolt/unittests/Core/MemoryMaps.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
5959
void initializeBOLT() {
6060
Relocation::Arch = ObjFile->makeTriple().getArch();
6161
BC = cantFail(BinaryContext::createBinaryContext(
62-
ObjFile->makeTriple(), ObjFile->getFileName(), nullptr, true,
62+
ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
63+
ObjFile->getFileName(), nullptr, true,
6364
DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
6465
ASSERT_FALSE(!BC);
6566
}

llvm/include/llvm/ExecutionEngine/JITLink/COFF.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace jitlink {
2424
/// its contents. The caller is responsible for ensuring that the object buffer
2525
/// outlives the graph.
2626
Expected<std::unique_ptr<LinkGraph>>
27-
createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer);
27+
createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer,
28+
std::shared_ptr<orc::SymbolStringPool> SSP);
2829

2930
/// Link the given graph.
3031
///

llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ namespace jitlink {
2323
/// Note: The graph does not take ownership of the underlying buffer, nor copy
2424
/// its contents. The caller is responsible for ensuring that the object buffer
2525
/// outlives the graph.
26-
Expected<std::unique_ptr<LinkGraph>>
27-
createLinkGraphFromCOFFObject_x86_64(MemoryBufferRef ObjectBuffer);
26+
Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromCOFFObject_x86_64(
27+
MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
2828

2929
/// jit-link the given object buffer, which must be a COFF x86-64 object file.
3030
void link_COFF_x86_64(std::unique_ptr<LinkGraph> G,

llvm/include/llvm/ExecutionEngine/JITLink/ELF.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace jitlink {
2424
/// its contents. The caller is responsible for ensuring that the object buffer
2525
/// outlives the graph.
2626
Expected<std::unique_ptr<LinkGraph>>
27-
createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer);
27+
createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer,
28+
std::shared_ptr<orc::SymbolStringPool> SSP);
2829

2930
/// Link the given graph.
3031
///

llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch32.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ namespace jitlink {
2424
/// Note: The graph does not take ownership of the underlying buffer, nor copy
2525
/// its contents. The caller is responsible for ensuring that the object buffer
2626
/// outlives the graph.
27-
Expected<std::unique_ptr<LinkGraph>>
28-
createLinkGraphFromELFObject_aarch32(MemoryBufferRef ObjectBuffer);
27+
Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch32(
28+
MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
2929

3030
/// jit-link the given object buffer, which must be an ELF arm/thumb object
3131
/// file.

llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ namespace jitlink {
2525
/// Note: The graph does not take ownership of the underlying buffer, nor copy
2626
/// its contents. The caller is responsible for ensuring that the object buffer
2727
/// outlives the graph.
28-
Expected<std::unique_ptr<LinkGraph>>
29-
createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer);
28+
Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64(
29+
MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
3030

3131
/// jit-link the given object buffer, which must be a ELF aarch64 relocatable
3232
/// object file.

llvm/include/llvm/ExecutionEngine/JITLink/ELF_i386.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ namespace jitlink {
2626
/// its contents. The caller is responsible for ensuring that the object buffer
2727
/// outlives the graph.
2828
Expected<std::unique_ptr<LinkGraph>>
29-
createLinkGraphFromELFObject_i386(MemoryBufferRef ObjectBuffer);
29+
createLinkGraphFromELFObject_i386(MemoryBufferRef ObjectBuffer,
30+
std::shared_ptr<orc::SymbolStringPool> SSP);
3031

3132
/// jit-link the given object buffer, which must be a ELF i386 relocatable
3233
/// object file.

llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ namespace jitlink {
2525
/// Note: The graph does not take ownership of the underlying buffer, nor copy
2626
/// its contents. The caller is responsible for ensuring that the object buffer
2727
/// outlives the graph.
28-
Expected<std::unique_ptr<LinkGraph>>
29-
createLinkGraphFromELFObject_loongarch(MemoryBufferRef ObjectBuffer);
28+
Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_loongarch(
29+
MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
3030

3131
/// jit-link the given object buffer, which must be an ELF loongarch object
3232
/// file.

llvm/include/llvm/ExecutionEngine/JITLink/ELF_ppc64.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ namespace llvm::jitlink {
2525
///
2626
/// WARNING: The big-endian backend has not been tested yet.
2727
Expected<std::unique_ptr<LinkGraph>>
28-
createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer);
28+
createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer,
29+
std::shared_ptr<orc::SymbolStringPool> SSP);
2930

3031
/// Create a LinkGraph from an ELF/ppc64le relocatable object.
3132
///
3233
/// Note: The graph does not take ownership of the underlying buffer, nor copy
3334
/// its contents. The caller is responsible for ensuring that the object buffer
3435
/// outlives the graph.
35-
Expected<std::unique_ptr<LinkGraph>>
36-
createLinkGraphFromELFObject_ppc64le(MemoryBufferRef ObjectBuffer);
36+
Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_ppc64le(
37+
MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
3738

3839
/// jit-link the given object buffer, which must be a ELF ppc64le object file.
3940
///

llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ namespace jitlink {
2626
/// its contents. The caller is responsible for ensuring that the object buffer
2727
/// outlives the graph.
2828
Expected<std::unique_ptr<LinkGraph>>
29-
createLinkGraphFromELFObject_riscv(MemoryBufferRef ObjectBuffer);
29+
createLinkGraphFromELFObject_riscv(MemoryBufferRef ObjectBuffer,
30+
std::shared_ptr<orc::SymbolStringPool> SSP);
3031

3132
/// jit-link the given object buffer, which must be a ELF riscv object file.
3233
void link_ELF_riscv(std::unique_ptr<LinkGraph> G,

llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace jitlink {
2424
/// its contents. The caller is responsible for ensuring that the object buffer
2525
/// outlives the graph.
2626
Expected<std::unique_ptr<LinkGraph>>
27-
createLinkGraphFromELFObject_x86_64(MemoryBufferRef ObjectBuffer);
27+
createLinkGraphFromELFObject_x86_64(MemoryBufferRef ObjectBuffer,
28+
std::shared_ptr<orc::SymbolStringPool> SSP);
2829

2930
/// jit-link the given object buffer, which must be a ELF x86-64 object file.
3031
void link_ELF_x86_64(std::unique_ptr<LinkGraph> G,

0 commit comments

Comments
 (0)