blob: 98ed4c3a6484b69c79c4874e877aedf9f8b59312 [file] [edit]
//===--- LinkGraphLinkingLayerTest.cpp - Unit tests for dep group calc ----===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/LinkGraphLinkingLayer.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::jitlink;
using namespace llvm::orc;
static const char BlockContentBytes[] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
static ArrayRef<char> BlockContent(BlockContentBytes);
/// Test wrapper class that accesses LinkGraphLinkingLayer's private members
/// via the friend declaration.
class LinkGraphLinkingLayerTests : public testing::Test {
protected:
using SymbolDepGroup = LinkGraphLinkingLayer::SymbolDepGroup;
static SmallVector<SymbolDepGroup> calculateDepGroups(LinkGraph &G) {
return LinkGraphLinkingLayer::calculateDepGroups(G);
}
static std::unique_ptr<LinkGraph> makeGraph(StringRef Name = "test") {
return std::make_unique<LinkGraph>(
Name.str(), std::make_shared<SymbolStringPool>(),
Triple("x86_64-apple-darwin"), 8, SubtargetFeatures(),
getGenericEdgeKindName);
}
};
// No blocks with non-local symbols: should produce no dep groups.
TEST_F(LinkGraphLinkingLayerTests, EmptyGraph) {
auto G = makeGraph();
auto DGs = calculateDepGroups(*G);
EXPECT_TRUE(DGs.empty());
}
// A single block with a defined symbol and no edges: no deps, so no dep group.
TEST_F(LinkGraphLinkingLayerTests, SingleBlockNoDeps) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &B =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
G->addDefinedSymbol(B, 0, "foo", 4, Linkage::Strong, Scope::Default, false,
true);
auto DGs = calculateDepGroups(*G);
EXPECT_TRUE(DGs.empty());
}
// A single block with a defined symbol that depends on an external symbol.
TEST_F(LinkGraphLinkingLayerTests, SingleBlockExternalDep) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &B =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
G->addDefinedSymbol(B, 0, "foo", 4, Linkage::Strong, Scope::Default, false,
true);
auto &ExtSym = G->addExternalSymbol("bar", 0, false);
B.addEdge(Edge::FirstRelocation, 0, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("foo"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}
// Two blocks, each with a defined symbol. Block A depends on Block B's symbol.
TEST_F(LinkGraphLinkingLayerTests, TwoBlocksDirectDep) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
// A -> B edge (through SymB).
BA.addEdge(Edge::FirstRelocation, 0, SymB, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
// The dep group should contain A's def and B as a dependency.
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&SymB));
}
// Two independent blocks with no edges between them: no dep groups.
TEST_F(LinkGraphLinkingLayerTests, TwoIndependentBlocks) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong, Scope::Default, false,
true);
auto DGs = calculateDepGroups(*G);
EXPECT_TRUE(DGs.empty());
}
// Block A -> anonymous block -> Block B. The anonymous block should be
// transparent, and A should transitively depend on B.
TEST_F(LinkGraphLinkingLayerTests, TransitiveThroughAnonymousBlock) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
// BAnon has no named symbols (anonymous block).
auto &AnonSym = G->addAnonymousSymbol(BAnon, 0, 4, false, true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
// A -> AnonSym (in BAnon), BAnon -> SymB (in BB).
BA.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BAnon.addEdge(Edge::FirstRelocation, 0, SymB, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&SymB));
}
// Block A depends on an external and an absolute symbol. Only the external
// should appear in deps (absolutes are assumed ready).
TEST_F(LinkGraphLinkingLayerTests, AbsoluteSymbolIgnored) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &B =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
G->addDefinedSymbol(B, 0, "foo", 4, Linkage::Strong, Scope::Default, false,
true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
auto &AbsSym = G->addAbsoluteSymbol("abs", ExecutorAddr(0x42), 0,
Linkage::Strong, Scope::Default, true);
B.addEdge(Edge::FirstRelocation, 0, ExtSym, 0);
B.addEdge(Edge::FirstRelocation, 4, AbsSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}
// Local-scoped symbols should not appear in dep groups.
TEST_F(LinkGraphLinkingLayerTests, LocalScopeSymbolsIgnored) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &B =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
G->addDefinedSymbol(B, 0, "local_sym", 4, Linkage::Strong, Scope::Local,
false, true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
B.addEdge(Edge::FirstRelocation, 0, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
// Local symbol shouldn't be tracked, so no dep group.
EXPECT_TRUE(DGs.empty());
}
// Multiple symbols in the same block should end up in the same dep group.
TEST_F(LinkGraphLinkingLayerTests, MultipleSymbolsSameBlock) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &B =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
G->addDefinedSymbol(B, 0, "foo", 4, Linkage::Strong, Scope::Default, false,
true);
G->addDefinedSymbol(B, 4, "bar", 4, Linkage::Strong, Scope::Default, false,
true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
B.addEdge(Edge::FirstRelocation, 0, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 2u);
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}
// A -> B -> A cycle (through named symbols). Named blocks don't participate
// in the SCC graph — only anonymous blocks do. So A and B each get their own
// dep group, with the other as a dependency.
TEST_F(LinkGraphLinkingLayerTests, CycleTwoNamedBlocks) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &SymA = G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong,
Scope::Default, false, true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
// A -> B, B -> A.
BA.addEdge(Edge::FirstRelocation, 0, SymB, 0);
BB.addEdge(Edge::FirstRelocation, 0, SymA, 0);
auto DGs = calculateDepGroups(*G);
// Each named block is its own SCC. A depends on B and B depends on A.
ASSERT_EQ(DGs.size(), 2u);
SymbolDepGroup *DGA = nullptr, *DGB = nullptr;
for (auto &DG : DGs) {
ASSERT_EQ(DG.Defs.size(), 1u);
if (DG.Defs[0]->getName() == G->intern("A"))
DGA = &DG;
else if (DG.Defs[0]->getName() == G->intern("B"))
DGB = &DG;
}
ASSERT_NE(DGA, nullptr);
ASSERT_NE(DGB, nullptr);
EXPECT_EQ(DGA->Deps.size(), 1u);
EXPECT_TRUE(DGA->Deps.count(&SymB));
EXPECT_EQ(DGB->Deps.size(), 1u);
EXPECT_TRUE(DGB->Deps.count(&SymA));
}
// A -> B -> A cycle where A also depends on external "ext". Since named
// blocks don't form SCCs, A and B get separate dep groups.
TEST_F(LinkGraphLinkingLayerTests, CycleWithExternalDep) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &SymA = G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong,
Scope::Default, false, true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, SymB, 0);
BA.addEdge(Edge::FirstRelocation, 4, ExtSym, 0);
BB.addEdge(Edge::FirstRelocation, 0, SymA, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 2u);
SymbolDepGroup *DGA = nullptr, *DGB = nullptr;
for (auto &DG : DGs) {
ASSERT_EQ(DG.Defs.size(), 1u);
if (DG.Defs[0]->getName() == G->intern("A"))
DGA = &DG;
else if (DG.Defs[0]->getName() == G->intern("B"))
DGB = &DG;
}
ASSERT_NE(DGA, nullptr);
ASSERT_NE(DGB, nullptr);
// A depends on B and ext.
EXPECT_EQ(DGA->Deps.size(), 2u);
EXPECT_TRUE(DGA->Deps.count(&SymB));
EXPECT_TRUE(DGA->Deps.count(&ExtSym));
// B depends on A.
EXPECT_EQ(DGB->Deps.size(), 1u);
EXPECT_TRUE(DGB->Deps.count(&SymA));
}
// Chain through multiple anonymous blocks:
// A -> anon1 -> anon2 -> B
TEST_F(LinkGraphLinkingLayerTests, ChainThroughMultipleAnonymousBlocks) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon1 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon2 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x4000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym1 = G->addAnonymousSymbol(BAnon1, 0, 4, false, true);
auto &AnonSym2 = G->addAnonymousSymbol(BAnon2, 0, 4, false, true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0);
BAnon1.addEdge(Edge::FirstRelocation, 0, AnonSym2, 0);
BAnon2.addEdge(Edge::FirstRelocation, 0, SymB, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&SymB));
}
// Diamond through anonymous blocks:
// A -> anon1 -> B
// A -> anon2 -> B
// A should depend on B (once).
TEST_F(LinkGraphLinkingLayerTests, DiamondThroughAnonymousBlocks) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon1 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon2 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x4000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym1 = G->addAnonymousSymbol(BAnon1, 0, 4, false, true);
auto &AnonSym2 = G->addAnonymousSymbol(BAnon2, 0, 4, false, true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0);
BA.addEdge(Edge::FirstRelocation, 4, AnonSym2, 0);
BAnon1.addEdge(Edge::FirstRelocation, 0, SymB, 0);
BAnon2.addEdge(Edge::FirstRelocation, 0, SymB, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&SymB));
}
// Cycle through anonymous blocks: A -> anon -> A.
// The anonymous block creates a path back to A, but since A is a named symbol
// the back-edge is a symbol dep (not an anonymous block dep). The SCC graph
// is A -> anon (no back-edge). The anon block's dep on A gets transitively
// merged into A's dep group, then filtered out (since A is a def), leaving
// an empty dep set. The group is then removed entirely.
TEST_F(LinkGraphLinkingLayerTests, CycleThroughAnonymousBlock) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &SymA = G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong,
Scope::Default, false, true);
auto &AnonSym = G->addAnonymousSymbol(BAnon, 0, 4, false, true);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BAnon.addEdge(Edge::FirstRelocation, 0, SymA, 0);
auto DGs = calculateDepGroups(*G);
// A's self-dep gets filtered, leaving an empty dep set. Groups with empty
// deps are removed, so no dep groups remain.
EXPECT_TRUE(DGs.empty());
}
// Cycle through anonymous blocks with an external dep hanging off the anon
// block: A -> anon -> A, anon -> ext. A should depend on ext.
TEST_F(LinkGraphLinkingLayerTests, CycleThroughAnonymousBlockWithExternalDep) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &SymA = G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong,
Scope::Default, false, true);
auto &AnonSym = G->addAnonymousSymbol(BAnon, 0, 4, false, true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BAnon.addEdge(Edge::FirstRelocation, 0, SymA, 0);
BAnon.addEdge(Edge::FirstRelocation, 4, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}
// Two separate dep groups: A -> ext1, B -> ext2 (no connection between A and
// B).
TEST_F(LinkGraphLinkingLayerTests, TwoSeparateDepGroups) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong, Scope::Default, false,
true);
auto &Ext1 = G->addExternalSymbol("ext1", 0, false);
auto &Ext2 = G->addExternalSymbol("ext2", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, Ext1, 0);
BB.addEdge(Edge::FirstRelocation, 0, Ext2, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 2u);
// Find which group is which (order may vary).
SymbolDepGroup *DGA = nullptr, *DGB = nullptr;
for (auto &DG : DGs) {
ASSERT_EQ(DG.Defs.size(), 1u);
if (DG.Defs[0]->getName() == G->intern("A"))
DGA = &DG;
else if (DG.Defs[0]->getName() == G->intern("B"))
DGB = &DG;
}
ASSERT_NE(DGA, nullptr);
ASSERT_NE(DGB, nullptr);
EXPECT_EQ(DGA->Deps.size(), 1u);
EXPECT_TRUE(DGA->Deps.count(&Ext1));
EXPECT_EQ(DGB->Deps.size(), 1u);
EXPECT_TRUE(DGB->Deps.count(&Ext2));
}
// Dep group merging: A and B both transitively depend on ext through the same
// anonymous block, and neither BA nor BB introduces any additional deps of its
// own. This allows the second block processed to merge into the first's dep
// group via the single-source-group fast path.
// A -> anon -> ext
// B -> anon -> ext (same anon block)
// A and B should be in the same dep group.
TEST_F(LinkGraphLinkingLayerTests, SharedAnonymousBlockMergesDepGroups) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym = G->addAnonymousSymbol(BAnon, 0, 4, false, true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BB.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BAnon.addEdge(Edge::FirstRelocation, 0, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 2u);
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}
// Same as SharedAnonymousBlockMergesDepGroups, but BA and BB each introduce
// an additional direct dep (even the same one). This prevents merging — each
// block triggers the general-case merge path and gets its own dep group.
// A -> anon -> ext1, A -> ext2
// B -> anon -> ext1, B -> ext2
TEST_F(LinkGraphLinkingLayerTests,
SharedAnonBlockWithExtraDepsSeparatesGroups) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym = G->addAnonymousSymbol(BAnon, 0, 4, false, true);
auto &Ext1 = G->addExternalSymbol("ext1", 0, false);
auto &Ext2 = G->addExternalSymbol("ext2", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BA.addEdge(Edge::FirstRelocation, 4, Ext2, 0);
BB.addEdge(Edge::FirstRelocation, 0, AnonSym, 0);
BB.addEdge(Edge::FirstRelocation, 4, Ext2, 0);
BAnon.addEdge(Edge::FirstRelocation, 0, Ext1, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 2u);
SymbolDepGroup *DGA = nullptr, *DGB = nullptr;
for (auto &DG : DGs) {
ASSERT_EQ(DG.Defs.size(), 1u);
if (DG.Defs[0]->getName() == G->intern("A"))
DGA = &DG;
else if (DG.Defs[0]->getName() == G->intern("B"))
DGB = &DG;
}
ASSERT_NE(DGA, nullptr);
ASSERT_NE(DGB, nullptr);
// Both groups should have ext1 (transitive) and ext2 (direct).
EXPECT_EQ(DGA->Deps.size(), 2u);
EXPECT_TRUE(DGA->Deps.count(&Ext1));
EXPECT_TRUE(DGA->Deps.count(&Ext2));
EXPECT_EQ(DGB->Deps.size(), 2u);
EXPECT_TRUE(DGB->Deps.count(&Ext1));
EXPECT_TRUE(DGB->Deps.count(&Ext2));
}
// Multi-node SCC in the anonymous block graph:
// A -> anon1 -> anon2 -> anon1 (cycle), anon2 -> ext
// anon1 and anon2 form a two-node SCC. A depends transitively on ext through
// that SCC.
TEST_F(LinkGraphLinkingLayerTests, MultiNodeSCC) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon1 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon2 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym1 = G->addAnonymousSymbol(BAnon1, 0, 4, false, true);
auto &AnonSym2 = G->addAnonymousSymbol(BAnon2, 0, 4, false, true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0);
BAnon1.addEdge(Edge::FirstRelocation, 0, AnonSym2, 0);
BAnon2.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0); // cycle
BAnon2.addEdge(Edge::FirstRelocation, 4, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 1u);
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}
// Multi-node SCC where different nodes in the cycle contribute different
// external deps:
// A -> anon1 -> anon2 -> anon1 (cycle), anon1 -> ext1, anon2 -> ext2
// Both ext1 and ext2 should be merged into A's dep group.
TEST_F(LinkGraphLinkingLayerTests, MultiNodeSCCMergesExternalDeps) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon1 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon2 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym1 = G->addAnonymousSymbol(BAnon1, 0, 4, false, true);
auto &AnonSym2 = G->addAnonymousSymbol(BAnon2, 0, 4, false, true);
auto &Ext1 = G->addExternalSymbol("ext1", 0, false);
auto &Ext2 = G->addExternalSymbol("ext2", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0);
BAnon1.addEdge(Edge::FirstRelocation, 0, AnonSym2, 0);
BAnon1.addEdge(Edge::FirstRelocation, 4, Ext1, 0);
BAnon2.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0); // cycle
BAnon2.addEdge(Edge::FirstRelocation, 4, Ext2, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 2u);
EXPECT_TRUE(DGs[0].Deps.count(&Ext1));
EXPECT_TRUE(DGs[0].Deps.count(&Ext2));
}
// Multi-node SCC where nodes in the cycle contribute a mix of external and
// named-block deps:
// A -> anon1 -> anon2 -> anon1 (cycle), anon1 -> B (named), anon2 -> ext
// A should depend on both B and ext.
TEST_F(LinkGraphLinkingLayerTests, MultiNodeSCCMergesMixedDeps) {
auto G = makeGraph();
auto &Sec = G->createSection("__data", MemProt::Read | MemProt::Write);
auto &BA =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x1000), 8, 0);
auto &BAnon1 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x2000), 8, 0);
auto &BAnon2 =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x3000), 8, 0);
auto &BB =
G->createContentBlock(Sec, BlockContent, ExecutorAddr(0x4000), 8, 0);
G->addDefinedSymbol(BA, 0, "A", 4, Linkage::Strong, Scope::Default, false,
true);
auto &AnonSym1 = G->addAnonymousSymbol(BAnon1, 0, 4, false, true);
auto &AnonSym2 = G->addAnonymousSymbol(BAnon2, 0, 4, false, true);
auto &SymB = G->addDefinedSymbol(BB, 0, "B", 4, Linkage::Strong,
Scope::Default, false, true);
auto &ExtSym = G->addExternalSymbol("ext", 0, false);
BA.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0);
BAnon1.addEdge(Edge::FirstRelocation, 0, AnonSym2, 0);
BAnon1.addEdge(Edge::FirstRelocation, 4, SymB, 0);
BAnon2.addEdge(Edge::FirstRelocation, 0, AnonSym1, 0); // cycle
BAnon2.addEdge(Edge::FirstRelocation, 4, ExtSym, 0);
auto DGs = calculateDepGroups(*G);
ASSERT_EQ(DGs.size(), 1u);
EXPECT_EQ(DGs[0].Defs.size(), 1u);
EXPECT_EQ(DGs[0].Defs[0]->getName(), G->intern("A"));
EXPECT_EQ(DGs[0].Deps.size(), 2u);
EXPECT_TRUE(DGs[0].Deps.count(&SymB));
EXPECT_TRUE(DGs[0].Deps.count(&ExtSym));
}