Skip to content

Commit 13ce9e6

Browse files
[JITLink][AArch32] Multi-stub support and selection armv7/thumbv7
1 parent 9730b7b commit 13ce9e6

File tree

5 files changed

+242
-71
lines changed

5 files changed

+242
-71
lines changed

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

Lines changed: 12 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -318,74 +318,32 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
318318
llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
319319
}
320320

321-
/// Stubs builder for a specific StubsFlavor
322-
///
323-
/// Right now we only have one default stub kind, but we want to extend this
324-
/// and allow creation of specific kinds in the future (e.g. branch range
325-
/// extension or interworking).
326-
///
321+
/// Stubs builder for v7 emits non-position-independent Arm and Thumb stubs.
327322
/// Let's keep it simple for the moment and not wire this through a GOT.
328323
///
329-
template <StubsFlavor Flavor>
330-
class StubsManager : public TableManager<StubsManager<Flavor>> {
324+
class StubsManager_v7 {
331325
public:
332-
StubsManager() = default;
333326

334-
/// Name of the object file section that will contain all our stubs.
335-
static StringRef getSectionName();
327+
StubsManager_v7() = default;
336328

337-
/// Implements link-graph traversal via visitExistingEdges().
338-
bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
339-
if (E.getTarget().isDefined())
340-
return false;
341-
342-
switch (E.getKind()) {
343-
case Thumb_Call:
344-
case Thumb_Jump24: {
345-
DEBUG_WITH_TYPE("jitlink", {
346-
dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
347-
<< B->getFixupAddress(E) << " (" << B->getAddress() << " + "
348-
<< formatv("{0:x}", E.getOffset()) << ")\n";
349-
});
350-
E.setTarget(this->getEntryForTarget(G, E.getTarget()));
351-
return true;
352-
}
353-
}
354-
return false;
329+
/// Name of the object file section that will contain all our stubs.
330+
static StringRef getSectionName() {
331+
return "__llvm_jitlink_aarch32_STUBS_v7";
355332
}
356333

357-
/// Create a branch range extension stub for the class's flavor.
358-
Symbol &createEntry(LinkGraph &G, Symbol &Target);
334+
/// Implements link-graph traversal via visitExistingEdges().
335+
bool visitEdge(LinkGraph &G, Block *B, Edge &E);
359336

360337
private:
361-
/// Create a new node in the link-graph for the given stub template.
362-
template <size_t Size>
363-
Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size],
364-
uint64_t Alignment) {
365-
ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
366-
return G.createContentBlock(getStubsSection(G), Template,
367-
orc::ExecutorAddr(), Alignment, 0);
368-
}
338+
using StubMapEntry = SmallVector<std::tuple<bool, Symbol *>>;
339+
enum ThumbStatePreference { Undefined = -1, Avoid = 0, PreferNot, Prefer, Force };
369340

370-
/// Get or create the object file section that will contain all our stubs.
371-
Section &getStubsSection(LinkGraph &G) {
372-
if (!StubsSection)
373-
StubsSection = &G.createSection(getSectionName(),
374-
orc::MemProt::Read | orc::MemProt::Exec);
375-
return *StubsSection;
376-
}
341+
Symbol *selectStub(const StubMapEntry &Candidates, ThumbStatePreference ThumbState);
377342

343+
DenseMap<StringRef, StubMapEntry> StubMap;
378344
Section *StubsSection = nullptr;
379345
};
380346

381-
/// Create a branch range extension stub with Thumb encoding for v7 CPUs.
382-
template <>
383-
Symbol &StubsManager<StubsFlavor::v7>::createEntry(LinkGraph &G, Symbol &Target);
384-
385-
template <> inline StringRef StubsManager<StubsFlavor::v7>::getSectionName() {
386-
return "__llvm_jitlink_aarch32_STUBS_Thumbv7";
387-
}
388-
389347
} // namespace aarch32
390348
} // namespace jitlink
391349
} // namespace llvm

llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,12 @@ class ELFLinkGraphBuilder_aarch32
216216
ArmCfg(std::move(ArmCfg)) {}
217217
};
218218

219-
template <aarch32::StubsFlavor Flavor>
219+
template <typename StubsManagerType>
220220
Error buildTables_ELF_aarch32(LinkGraph &G) {
221221
LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
222222

223-
aarch32::StubsManager<Flavor> PLT;
224-
visitExistingEdges(G, PLT);
223+
StubsManagerType Stubs;
224+
visitExistingEdges(G, Stubs);
225225
return Error::success();
226226
}
227227

@@ -311,7 +311,7 @@ void link_ELF_aarch32(std::unique_ptr<LinkGraph> G,
311311
switch (ArmCfg.Stubs) {
312312
case aarch32::StubsFlavor::v7:
313313
PassCfg.PostPrunePasses.push_back(
314-
buildTables_ELF_aarch32<aarch32::StubsFlavor::v7>);
314+
buildTables_ELF_aarch32<aarch32::StubsManager_v7>);
315315
break;
316316
case aarch32::StubsFlavor::Unsupported:
317317
llvm_unreachable("Check before building graph");

llvm/lib/ExecutionEngine/JITLink/aarch32.cpp

Lines changed: 131 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/StringExtras.h"
1616
#include "llvm/BinaryFormat/ELF.h"
1717
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
18+
#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
1819
#include "llvm/Object/ELFObjectFile.h"
1920
#include "llvm/Support/Endian.h"
2021
#include "llvm/Support/ManagedStatic.h"
@@ -678,28 +679,145 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
678679
}
679680
}
680681

682+
const uint8_t Armv7ABS[] = {
683+
0x00, 0xc0, 0x00, 0xe3, // movw r12, #0x0000 ; lower 16-bit
684+
0x00, 0xc0, 0x40, 0xe3, // movt r12, #0x0000 ; upper 16-bit
685+
0x1c, 0xff, 0x2f, 0xe1 // bx r12
686+
};
687+
681688
const uint8_t Thumbv7ABS[] = {
682689
0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000 ; lower 16-bit
683690
0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000 ; upper 16-bit
684691
0x60, 0x47 // bx r12
685692
};
686693

687-
template <>
688-
Symbol &StubsManager<StubsFlavor::v7>::createEntry(LinkGraph &G, Symbol &Target) {
694+
/// Create a new node in the link-graph for the given stub template.
695+
template <size_t Size>
696+
static Block &allocStub(LinkGraph &G, Section &S, const uint8_t (&Code)[Size]) {
689697
constexpr uint64_t Alignment = 4;
690-
Block &B = addStub(G, Thumbv7ABS, Alignment);
691-
LLVM_DEBUG({
692-
const char *StubPtr = B.getContent().data();
693-
HalfWords Reg12 = encodeRegMovtT1MovwT3(12);
694-
assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) &&
695-
checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) &&
696-
"Linker generated stubs may only corrupt register r12 (IP)");
697-
});
698+
ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
699+
return G.createContentBlock(S, Template, orc::ExecutorAddr(), Alignment, 0);
700+
}
701+
702+
static Block &createStubThumbv7(LinkGraph &G, Section &S, Symbol &Target) {
703+
Block &B = allocStub(G, S, Thumbv7ABS);
698704
B.addEdge(Thumb_MovwAbsNC, 0, Target, 0);
699705
B.addEdge(Thumb_MovtAbs, 4, Target, 0);
700-
Symbol &Stub = G.addAnonymousSymbol(B, 0, B.getSize(), true, false);
701-
Stub.setTargetFlags(ThumbSymbol);
702-
return Stub;
706+
707+
[[maybe_unused]] const char *StubPtr = B.getContent().data();
708+
[[maybe_unused]] HalfWords Reg12 = encodeRegMovtT1MovwT3(12);
709+
assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) &&
710+
checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) &&
711+
"Linker generated stubs may only corrupt register r12 (IP)");
712+
return B;
713+
}
714+
715+
static Block &createStubArmv7(LinkGraph &G, Section &S, Symbol &Target) {
716+
Block &B = allocStub(G, S, Armv7ABS);
717+
B.addEdge(Arm_MovwAbsNC, 0, Target, 0);
718+
B.addEdge(Arm_MovtAbs, 4, Target, 0);
719+
720+
[[maybe_unused]] const char *StubPtr = B.getContent().data();
721+
[[maybe_unused]] uint32_t Reg12 = encodeRegMovtA1MovwA2(12);
722+
assert(checkRegister<Arm_MovwAbsNC>(StubPtr, Reg12) &&
723+
checkRegister<Arm_MovtAbs>(StubPtr + 4, Reg12) &&
724+
"Linker generated stubs may only corrupt register r12 (IP)");
725+
return B;
726+
}
727+
728+
Symbol *StubsManager_v7::selectStub(const StubMapEntry &Candidates,
729+
ThumbStatePreference ThumbState) {
730+
Symbol *StubSymbol = nullptr;
731+
for (auto [StubIsThumb, Sym] : Candidates) {
732+
if (StubIsThumb) {
733+
if (ThumbState == Avoid)
734+
continue; // No match
735+
StubSymbol = Sym;
736+
break; // Best match
737+
}
738+
if (ThumbState == Avoid) {
739+
StubSymbol = Sym;
740+
break; // Best match
741+
}
742+
if (ThumbState == Prefer) {
743+
StubSymbol = Sym;
744+
continue; // Acceptable match
745+
}
746+
}
747+
return StubSymbol;
748+
}
749+
750+
bool StubsManager_v7::visitEdge(LinkGraph &G, Block *B, Edge &E) {
751+
ThumbStatePreference ThumbState = Undefined;
752+
switch (E.getKind()) {
753+
case Arm_Call:
754+
case Arm_Jump24:
755+
ThumbState = Avoid;
756+
break;
757+
case Thumb_Call:
758+
ThumbState = Prefer;
759+
break;
760+
case Thumb_Jump24:
761+
ThumbState = Force;
762+
break;
763+
default:
764+
return false;
765+
}
766+
767+
assert(ThumbState != Undefined && "Define Thumb state preference");
768+
769+
// Stubs are usually created only for external targets.
770+
Symbol &Target = E.getTarget();
771+
if (Target.isDefined()) {
772+
bool TargetIsThumb = Target.getTargetFlags() & ThumbSymbol;
773+
bool InterworkingStub = ThumbState == Force && !TargetIsThumb;
774+
if (!InterworkingStub)
775+
return false;
776+
}
777+
778+
LLVM_DEBUG(dbgs() << " Preparing stub with thumb-state '"
779+
<< (ThumbState == Avoid ? "Avoid" : "")
780+
<< (ThumbState == Prefer ? "Prefer" : "")
781+
<< (ThumbState == Force ? "Force" : "") << "' for "
782+
<< G.getEdgeKindName(E.getKind()) << " edge at "
783+
<< B->getFixupAddress(E) << " (" << B->getAddress() << " + "
784+
<< formatv("{0:x}", E.getOffset()) << ")\n");
785+
786+
assert(Target.hasName() && "Edge cannot point to anonymous target");
787+
auto TargetEntry = StubMap.try_emplace(Target.getName()).first;
788+
789+
Symbol *StubSymbol = selectStub(TargetEntry->second, ThumbState);
790+
if (!StubSymbol) {
791+
if (!StubsSection)
792+
StubsSection = &G.createSection(getSectionName(),
793+
orc::MemProt::Read | orc::MemProt::Exec);
794+
795+
bool MakeThumb = ThumbState >= Prefer;
796+
Block &B = MakeThumb ? createStubThumbv7(G, *StubsSection, Target)
797+
: createStubArmv7(G, *StubsSection, Target);
798+
799+
StubSymbol = &G.addAnonymousSymbol(B, 0, B.getSize(), true, false);
800+
if (MakeThumb)
801+
StubSymbol->setTargetFlags(ThumbSymbol);
802+
803+
LLVM_DEBUG({
804+
dbgs() << " Created " << (MakeThumb ? "Thumb" : "Arm") << " entry for "
805+
<< Target.getName() << " in " << StubsSection->getName() << ": "
806+
<< *StubSymbol << "\n";
807+
});
808+
809+
TargetEntry->second.emplace_back(MakeThumb, StubSymbol);
810+
}
811+
812+
LLVM_DEBUG({
813+
bool StubIsThumb = StubSymbol->getTargetFlags() & ThumbSymbol;
814+
dbgs() << " Using " << (StubIsThumb ? "Thumb" : "Arm") << " entry "
815+
<< *StubSymbol << " in "
816+
<< StubSymbol->getBlock().getSection().getName() << "\n";
817+
});
818+
819+
E.setTarget(*StubSymbol);
820+
return true;
703821
}
704822

705823
const char *getEdgeKindName(Edge::Kind K) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# RUN: rm -rf %t && mkdir -p %t
2+
# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \
3+
# RUN: -filetype=obj -filetype=obj -o %t/elf_stubs.o %s
4+
# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
5+
# RUN: -slab-allocate 10Kb -slab-page-size 4096 \
6+
# RUN: -abs ext_arm=0x76bbe880 \
7+
# RUN: -check %s %t/elf_stubs.o
8+
9+
.text
10+
.syntax unified
11+
12+
# Check that calls/jumps to external functions trigger the generation of
13+
# branch-range extension stubs. These stubs don't follow the default PLT model
14+
# where the branch-target address is loaded from a GOT entry. Instead, they
15+
# hard-code it in the immediate field.
16+
#
17+
# jitlink-check: decode_operand(test_external_call, 0) = stub_addr(elf_stubs.o, ext_arm) - (test_external_call + 8)
18+
# jitlink-check: decode_operand(test_external_jump, 0) = stub_addr(elf_stubs.o, ext_arm) - (test_external_jump + 8)
19+
.globl test_external_call
20+
.type test_external_call,%function
21+
.p2align 2
22+
test_external_call:
23+
bl ext_arm
24+
.size test_external_call, .-test_external_call
25+
26+
.globl test_external_jump
27+
.type test_external_jump,%function
28+
.p2align 2
29+
test_external_jump:
30+
b ext_arm
31+
.size test_external_jump, .-test_external_jump
32+
33+
# Empty main function for jitlink to be happy
34+
.globl main
35+
.type main,%function
36+
.p2align 2
37+
main:
38+
bx lr
39+
.size main, .-main
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# RUN: rm -rf %t && mkdir -p %t
2+
# RUN: llvm-mc -triple=thumbv7-linux-gnueabi -arm-add-build-attributes \
3+
# RUN: -filetype=obj -filetype=obj -o %t/elf_stubs.o %s
4+
# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
5+
# RUN: -slab-allocate 10Kb -slab-page-size 4096 \
6+
# RUN: -abs external_func=0x76bbe880 \
7+
# RUN: -check %s %t/elf_stubs.o
8+
9+
.text
10+
.syntax unified
11+
12+
# Check support for multiple stubs for a single symbol
13+
14+
# jitlink-check: decode_operand(test_avoid_thumb, 0) = stub_addr(elf_stubs.o, external_func, 0) - (test_avoid_thumb + 8)
15+
# jitlink-check: decode_operand(test_prefer_thumb, 2) = stub_addr(elf_stubs.o, external_func, 0) - next_pc(test_prefer_thumb)
16+
# jitlink-check: decode_operand(test_force_thumb, 0) = stub_addr(elf_stubs.o, external_func, 1) - next_pc(test_force_thumb)
17+
18+
# Arm call will create an Arm stub first
19+
.globl test_avoid_thumb
20+
.type test_avoid_thumb,%function
21+
.p2align 2
22+
.code 32
23+
test_avoid_thumb:
24+
bl external_func
25+
.size test_avoid_thumb, .-test_avoid_thumb
26+
27+
# Thumb call would create a Thumb stub if there was non at all, but it can be
28+
# rewritten to BLX and reuse the existing Arm stub.
29+
# TODO: Should we reconsider during relaxation?
30+
.globl test_prefer_thumb
31+
.type test_prefer_thumb,%function
32+
.p2align 1
33+
.code 16
34+
.thumb_func
35+
test_prefer_thumb:
36+
bl external_func
37+
.size test_prefer_thumb, .-test_prefer_thumb
38+
39+
# Thumb jump requires a Thumb stub, so it creates one in addition to the
40+
# existing Arm stub.
41+
.globl test_force_thumb
42+
.type test_force_thumb,%function
43+
.p2align 1
44+
.code 16
45+
.thumb_func
46+
test_force_thumb:
47+
b external_func
48+
.size test_force_thumb, .-test_force_thumb
49+
50+
# Empty main function for jitlink to be happy
51+
.globl main
52+
.type main,%function
53+
.p2align 2
54+
main:
55+
bx lr
56+
.size main, .-main

0 commit comments

Comments
 (0)