Skip to content

Commit 248a268

Browse files
author
git apple-llvm automerger
committed
Merge commit '154da86472ab' from apple/master into swift/master-next
2 parents 7827ba6 + 154da86 commit 248a268

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class Edge {
7272
: Target(&Target), Offset(Offset), Addend(Addend), K(K) {}
7373

7474
OffsetT getOffset() const { return Offset; }
75+
void setOffset(OffsetT Offset) { this->Offset = Offset; }
7576
Kind getKind() const { return K; }
7677
void setKind(Kind K) { this->K = K; }
7778
bool isRelocation() const { return K >= FirstRelocation; }
@@ -208,15 +209,32 @@ class Block : public Addressable {
208209
/// Get the alignment for this content.
209210
uint64_t getAlignment() const { return 1ull << P2Align; }
210211

212+
/// Set the alignment for this content.
213+
void setAlignment(uint64_t Alignment) {
214+
assert(isPowerOf2_64(Alignment) && "Alignment must be a power of two");
215+
P2Align = Alignment ? countTrailingZeros(Alignment) : 0;
216+
}
217+
211218
/// Get the alignment offset for this content.
212219
uint64_t getAlignmentOffset() const { return AlignmentOffset; }
213220

221+
/// Set the alignment offset for this content.
222+
void setAlignmentOffset(uint64_t AlignmentOffset) {
223+
assert(AlignmentOffset < (1ull << P2Align) &&
224+
"Alignment offset can't exceed alignment");
225+
this->AlignmentOffset = AlignmentOffset;
226+
}
227+
214228
/// Add an edge to this block.
215229
void addEdge(Edge::Kind K, Edge::OffsetT Offset, Symbol &Target,
216230
Edge::AddendT Addend) {
217231
Edges.push_back(Edge(K, Offset, Target, Addend));
218232
}
219233

234+
/// Add an edge by copying an existing one. This is typically used when
235+
/// moving edges between blocks.
236+
void addEdge(const Edge &E) { Edges.push_back(E); }
237+
220238
/// Return the list of edges attached to this content.
221239
iterator_range<edge_iterator> edges() {
222240
return make_range(Edges.begin(), Edges.end());
@@ -233,6 +251,10 @@ class Block : public Addressable {
233251
/// Returns true if the list of edges is empty.
234252
bool edges_empty() const { return Edges.empty(); }
235253

254+
/// Remove the edge pointed to by the given iterator.
255+
/// Invalidates all iterators that point to or past the given one.
256+
void removeEdge(const_edge_iterator I) { Edges.erase(I); }
257+
236258
private:
237259
static constexpr uint64_t MaxAlignmentOffset = (1ULL << 57) - 1;
238260

@@ -287,6 +309,7 @@ class Symbol {
287309
JITTargetAddress Size, Linkage L, Scope S, bool IsLive,
288310
bool IsCallable)
289311
: Name(Name), Base(&Base), Offset(Offset), Size(Size) {
312+
assert(Offset <= MaxOffset && "Offset out of range");
290313
setLinkage(L);
291314
setScope(S);
292315
setLive(IsLive);
@@ -484,6 +507,13 @@ class Symbol {
484507
// note: Size and IsCallable fields left unchanged.
485508
}
486509

510+
void setBlock(Block &B) { Base = &B; }
511+
512+
void setOffset(uint64_t NewOffset) {
513+
assert(NewOffset <= MaxOffset && "Offset out of range");
514+
Offset = NewOffset;
515+
}
516+
487517
static constexpr uint64_t MaxOffset = (1ULL << 59) - 1;
488518

489519
// FIXME: A char* or SymbolStringPtr may pack better.
@@ -743,6 +773,29 @@ class LinkGraph {
743773
Alignment, AlignmentOffset);
744774
}
745775

776+
/// Cache type for the splitBlock function.
777+
using SplitBlockCache = Optional<SmallVector<Symbol *, 8>>;
778+
779+
/// Splits block B at the given index which must be greater than zero.
780+
/// If SplitIndex == B.getSize() then this function is a no-op and returns B.
781+
/// If SplitIndex < B.getSize() then this function returns a new block
782+
/// covering the range [ 0, SplitIndex ), and B is modified to cover the range
783+
/// [ SplitIndex, B.size() ).
784+
///
785+
/// The optional Cache parameter can be used to speed up repeated calls to
786+
/// splitBlock for a single block. If the value is None the cache will be
787+
/// treated as uninitialized and splitBlock will populate it. Otherwise it
788+
/// is assumed to contain the list of Symbols pointing at B, sorted in
789+
/// descending order of offset.
790+
///
791+
/// Note: The cache is not automatically updated if new symbols are introduced
792+
/// between calls to splitBlock. Any newly introduced symbols may be
793+
/// added to the cache manually (descending offset order must be
794+
/// preserved), or the cache can be set to None and rebuilt by
795+
/// splitBlock on the next call.
796+
Block &splitBlock(Block &B, size_t SplitIndex,
797+
SplitBlockCache *Cache = nullptr);
798+
746799
/// Add an external symbol.
747800
/// Some formats (e.g. ELF) allow Symbols to have sizes. For Symbols whose
748801
/// size is not known, you should substitute '0'.

llvm/lib/ExecutionEngine/JITLink/JITLink.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,85 @@ LinkGraph::~LinkGraph() {
153153
B->~Block();
154154
}
155155

156+
Block &LinkGraph::splitBlock(Block &B, size_t SplitIndex,
157+
SplitBlockCache *Cache) {
158+
159+
assert(SplitIndex > 0 && "splitBlock can not be called with SplitIndex == 0");
160+
161+
// If the split point covers all of B then just return B.
162+
if (SplitIndex == B.getSize())
163+
return B;
164+
165+
assert(SplitIndex < B.getSize() && "SplitIndex out of range");
166+
167+
// Create the new block covering [ 0, SplitIndex ).
168+
auto &NewBlock =
169+
B.isZeroFill()
170+
? createZeroFillBlock(B.getSection(), SplitIndex, B.getAddress(),
171+
B.getAlignment(), B.getAlignmentOffset())
172+
: createContentBlock(
173+
B.getSection(), B.getContent().substr(0, SplitIndex),
174+
B.getAddress(), B.getAlignment(), B.getAlignmentOffset());
175+
176+
// Modify B to cover [ SplitIndex, B.size() ).
177+
B.setAddress(B.getAddress() + SplitIndex);
178+
B.setContent(B.getContent().substr(SplitIndex));
179+
B.setAlignmentOffset((B.getAlignmentOffset() + SplitIndex) %
180+
B.getAlignment());
181+
182+
// Handle edge transfer/update.
183+
{
184+
// Copy edges to NewBlock (recording their iterators so that we can remove
185+
// them from B), and update of Edges remaining on B.
186+
std::vector<Block::edge_iterator> EdgesToRemove;
187+
for (auto I = B.edges().begin(), E = B.edges().end(); I != E; ++I) {
188+
if (I->getOffset() < SplitIndex) {
189+
NewBlock.addEdge(*I);
190+
EdgesToRemove.push_back(I);
191+
} else
192+
I->setOffset(I->getOffset() - SplitIndex);
193+
}
194+
195+
// Remove edges that were transfered to NewBlock from B.
196+
while (!EdgesToRemove.empty()) {
197+
B.removeEdge(EdgesToRemove.back());
198+
EdgesToRemove.pop_back();
199+
}
200+
}
201+
202+
// Handle symbol transfer/update.
203+
{
204+
// Initialize the symbols cache if necessary.
205+
SplitBlockCache LocalBlockSymbolsCache;
206+
if (!Cache)
207+
Cache = &LocalBlockSymbolsCache;
208+
if (*Cache == None) {
209+
*Cache = SplitBlockCache::value_type();
210+
for (auto *Sym : B.getSection().symbols())
211+
if (&Sym->getBlock() == &B)
212+
(*Cache)->push_back(Sym);
213+
214+
llvm::sort(**Cache, [](const Symbol *LHS, const Symbol *RHS) {
215+
return LHS->getOffset() > RHS->getOffset();
216+
});
217+
}
218+
auto &BlockSymbols = **Cache;
219+
220+
// Transfer all symbols with offset less than SplitIndex to NewBlock.
221+
while (!BlockSymbols.empty() &&
222+
BlockSymbols.back()->getOffset() < SplitIndex) {
223+
BlockSymbols.back()->setBlock(NewBlock);
224+
BlockSymbols.pop_back();
225+
}
226+
227+
// Update offsets for all remaining symbols in B.
228+
for (auto *Sym : BlockSymbols)
229+
Sym->setOffset(Sym->getOffset() - SplitIndex);
230+
}
231+
232+
return NewBlock;
233+
}
234+
156235
void LinkGraph::dump(raw_ostream &OS,
157236
std::function<StringRef(Edge::Kind)> EdgeKindToName) {
158237
if (!EdgeKindToName)

llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(LLVM_LINK_COMPONENTS
1212

1313
add_llvm_unittest(JITLinkTests
1414
JITLinkTestCommon.cpp
15+
LinkGraphTests.cpp
1516
MachO_x86_64_Tests.cpp
1617
)
1718

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//===------ LinkGraphTests.cpp - Unit tests for core JITLink classes ------===//
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/ExecutionEngine/JITLink/JITLink.h"
10+
#include "llvm/Support/Endian.h"
11+
#include "llvm/Support/Memory.h"
12+
#include "gtest/gtest.h"
13+
14+
using namespace llvm;
15+
using namespace llvm::jitlink;
16+
17+
TEST(LinkGraphTest, Construction) {
18+
// Check that LinkGraph construction works as expected.
19+
LinkGraph G("foo", 8, support::little);
20+
EXPECT_EQ(G.getName(), "foo");
21+
EXPECT_EQ(G.getPointerSize(), 8U);
22+
EXPECT_EQ(G.getEndianness(), support::little);
23+
EXPECT_TRUE(empty(G.external_symbols()));
24+
EXPECT_TRUE(empty(G.absolute_symbols()));
25+
EXPECT_TRUE(empty(G.defined_symbols()));
26+
EXPECT_TRUE(empty(G.blocks()));
27+
}
28+
29+
TEST(LinkGraphTest, SplitBlock) {
30+
// Check that the LinkGraph::splitBlock test works as expected.
31+
32+
const char BlockContentBytes[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
33+
0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
34+
0x1C, 0x1D, 0x1E, 0x1F, 0x00};
35+
StringRef BlockContent(BlockContentBytes);
36+
37+
LinkGraph G("foo", 8, support::little);
38+
auto &Sec = G.createSection(
39+
"test", sys::Memory::ProtectionFlags(sys::Memory::MF_READ |
40+
sys::Memory::MF_WRITE));
41+
42+
// Create the block to split.
43+
auto &B1 = G.createContentBlock(Sec, BlockContent, 0x1000, 8, 0);
44+
45+
// Add some symbols to the block.
46+
auto &S1 = G.addDefinedSymbol(B1, 0, "S1", 4, Linkage::Strong, Scope::Default,
47+
false, false);
48+
auto &S2 = G.addDefinedSymbol(B1, 4, "S2", 4, Linkage::Strong, Scope::Default,
49+
false, false);
50+
auto &S3 = G.addDefinedSymbol(B1, 8, "S3", 4, Linkage::Strong, Scope::Default,
51+
false, false);
52+
auto &S4 = G.addDefinedSymbol(B1, 12, "S4", 4, Linkage::Strong,
53+
Scope::Default, false, false);
54+
55+
// Add an extra block, EB, and target symbols, and use these to add edges
56+
// from B1 to EB.
57+
auto &EB = G.createContentBlock(Sec, BlockContent, 0x2000, 8, 0);
58+
auto &ES1 = G.addDefinedSymbol(EB, 0, "TS1", 4, Linkage::Strong,
59+
Scope::Default, false, false);
60+
auto &ES2 = G.addDefinedSymbol(EB, 4, "TS2", 4, Linkage::Strong,
61+
Scope::Default, false, false);
62+
auto &ES3 = G.addDefinedSymbol(EB, 8, "TS3", 4, Linkage::Strong,
63+
Scope::Default, false, false);
64+
auto &ES4 = G.addDefinedSymbol(EB, 12, "TS4", 4, Linkage::Strong,
65+
Scope::Default, false, false);
66+
67+
// Add edges from B1 to EB.
68+
B1.addEdge(Edge::FirstRelocation, 0, ES1, 0);
69+
B1.addEdge(Edge::FirstRelocation, 4, ES2, 0);
70+
B1.addEdge(Edge::FirstRelocation, 8, ES3, 0);
71+
B1.addEdge(Edge::FirstRelocation, 12, ES4, 0);
72+
73+
// Split B1.
74+
auto &B2 = G.splitBlock(B1, 8);
75+
76+
// Check that the block addresses and content matches what we would expect.
77+
EXPECT_EQ(B1.getAddress(), 0x1008U);
78+
EXPECT_EQ(B1.getContent(), BlockContent.substr(8));
79+
80+
EXPECT_EQ(B2.getAddress(), 0x1000U);
81+
EXPECT_EQ(B2.getContent(), BlockContent.substr(0, 8));
82+
83+
// Check that symbols in B1 were transferred as expected:
84+
// We expect S1 and S2 to have been transferred to B2, and S3 and S4 to have
85+
// remained attached to B1. Symbols S3 and S4 should have had their offsets
86+
// slid to account for the change in address of B2.
87+
EXPECT_EQ(&S1.getBlock(), &B2);
88+
EXPECT_EQ(S1.getOffset(), 0U);
89+
90+
EXPECT_EQ(&S2.getBlock(), &B2);
91+
EXPECT_EQ(S2.getOffset(), 4U);
92+
93+
EXPECT_EQ(&S3.getBlock(), &B1);
94+
EXPECT_EQ(S3.getOffset(), 0U);
95+
96+
EXPECT_EQ(&S4.getBlock(), &B1);
97+
EXPECT_EQ(S4.getOffset(), 4U);
98+
99+
// Check that edges in B1 have been transferred as expected:
100+
// Both blocks should now have two edges each at offsets 0 and 4.
101+
EXPECT_EQ(size(B1.edges()), 2);
102+
if (size(B1.edges()) == 2) {
103+
auto *E1 = &*B1.edges().begin();
104+
auto *E2 = &*(B1.edges().begin() + 1);
105+
if (E2->getOffset() < E1->getOffset())
106+
std::swap(E1, E2);
107+
EXPECT_EQ(E1->getOffset(), 0U);
108+
EXPECT_EQ(E2->getOffset(), 4U);
109+
}
110+
111+
EXPECT_EQ(size(B2.edges()), 2);
112+
if (size(B2.edges()) == 2) {
113+
auto *E1 = &*B2.edges().begin();
114+
auto *E2 = &*(B2.edges().begin() + 1);
115+
if (E2->getOffset() < E1->getOffset())
116+
std::swap(E1, E2);
117+
EXPECT_EQ(E1->getOffset(), 0U);
118+
EXPECT_EQ(E2->getOffset(), 4U);
119+
}
120+
}

0 commit comments

Comments
 (0)