Skip to content

Commit 35b2b24

Browse files
MikaelSmithlhames
andauthored
Implement reserveAllocationSpace for SectionMemoryManager (llvm#71968)
Implements `reserveAllocationSpace` and provides an option to enable `needsToReserveAllocationSpace` for large-memory environments with AArch64. The [AArch64 ABI](https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst#7code-models) has restrictions on the distance between TEXT and GOT sections as the instructions to reference them are limited to 2 or 4GB. Allocating sections in multiple blocks can result in distances greater than that on systems with lots of memory. In those environments several projects using SectionMemoryManager with MCJIT have run across assertion failures for the R_AARCH64_ADR_PREL_PG_HI21 instruction as it attempts to address across distances greater than 2GB (an int32). Fixes llvm#71963 by allocating all sections in a single contiguous memory allocation, limiting the distance required for instruction offsets similar to how pre-compiled binaries would be loaded into memory. Co-authored-by: Lang Hames <[email protected]>
1 parent e88f3d8 commit 35b2b24

File tree

3 files changed

+363
-15
lines changed

3 files changed

+363
-15
lines changed

llvm/include/llvm/ExecutionEngine/SectionMemoryManager.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,24 @@ class LLVM_ABI SectionMemoryManager : public RTDyldMemoryManager {
105105
/// Creates a SectionMemoryManager instance with \p MM as the associated
106106
/// memory mapper. If \p MM is nullptr then a default memory mapper is used
107107
/// that directly calls into the operating system.
108-
SectionMemoryManager(MemoryMapper *MM = nullptr);
108+
///
109+
/// If \p ReserveAlloc is true all memory will be pre-allocated, and any
110+
/// attempts to allocate beyond pre-allocated memory will fail.
111+
SectionMemoryManager(MemoryMapper *MM = nullptr, bool ReserveAlloc = false);
109112
SectionMemoryManager(const SectionMemoryManager &) = delete;
110113
void operator=(const SectionMemoryManager &) = delete;
111114
~SectionMemoryManager() override;
112115

116+
/// Enable reserveAllocationSpace when requested.
117+
bool needsToReserveAllocationSpace() override { return ReserveAllocation; }
118+
119+
/// Implements allocating all memory in a single block. This is required to
120+
/// limit memory offsets to fit the ARM ABI; large memory systems may
121+
/// otherwise allocate separate sections too far apart.
122+
void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign,
123+
uintptr_t RODataSize, Align RODataAlign,
124+
uintptr_t RWDataSize, Align RWDataAlign) override;
125+
113126
/// Allocates a memory block of (at least) the given size suitable for
114127
/// executable code.
115128
///
@@ -181,13 +194,16 @@ class LLVM_ABI SectionMemoryManager : public RTDyldMemoryManager {
181194
std::error_code applyMemoryGroupPermissions(MemoryGroup &MemGroup,
182195
unsigned Permissions);
183196

197+
bool hasSpace(const MemoryGroup &MemGroup, uintptr_t Size) const;
198+
184199
void anchor() override;
185200

186201
MemoryGroup CodeMem;
187202
MemoryGroup RWDataMem;
188203
MemoryGroup RODataMem;
189204
MemoryMapper *MMapper;
190205
std::unique_ptr<MemoryMapper> OwnedMMapper;
206+
bool ReserveAllocation;
191207
};
192208

193209
} // end namespace llvm

llvm/lib/ExecutionEngine/SectionMemoryManager.cpp

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,97 @@
1717

1818
namespace llvm {
1919

20+
bool SectionMemoryManager::hasSpace(const MemoryGroup &MemGroup,
21+
uintptr_t Size) const {
22+
for (const FreeMemBlock &FreeMB : MemGroup.FreeMem) {
23+
if (FreeMB.Free.allocatedSize() >= Size)
24+
return true;
25+
}
26+
return false;
27+
}
28+
29+
void SectionMemoryManager::reserveAllocationSpace(
30+
uintptr_t CodeSize, Align CodeAlign, uintptr_t RODataSize,
31+
Align RODataAlign, uintptr_t RWDataSize, Align RWDataAlign) {
32+
if (CodeSize == 0 && RODataSize == 0 && RWDataSize == 0)
33+
return;
34+
35+
static const size_t PageSize = sys::Process::getPageSizeEstimate();
36+
37+
// Code alignment needs to be at least the stub alignment - however, we
38+
// don't have an easy way to get that here so as a workaround, we assume
39+
// it's 8, which is the largest value I observed across all platforms.
40+
constexpr uint64_t StubAlign = 8;
41+
CodeAlign = Align(std::max(CodeAlign.value(), StubAlign));
42+
RODataAlign = Align(std::max(RODataAlign.value(), StubAlign));
43+
RWDataAlign = Align(std::max(RWDataAlign.value(), StubAlign));
44+
45+
// Get space required for each section. Use the same calculation as
46+
// allocateSection because we need to be able to satisfy it.
47+
uint64_t RequiredCodeSize = alignTo(CodeSize, CodeAlign) + CodeAlign.value();
48+
uint64_t RequiredRODataSize =
49+
alignTo(RODataSize, RODataAlign) + RODataAlign.value();
50+
uint64_t RequiredRWDataSize =
51+
alignTo(RWDataSize, RWDataAlign) + RWDataAlign.value();
52+
53+
if (hasSpace(CodeMem, RequiredCodeSize) &&
54+
hasSpace(RODataMem, RequiredRODataSize) &&
55+
hasSpace(RWDataMem, RequiredRWDataSize)) {
56+
// Sufficient space in contiguous block already available.
57+
return;
58+
}
59+
60+
// MemoryManager does not have functions for releasing memory after it's
61+
// allocated. Normally it tries to use any excess blocks that were allocated
62+
// due to page alignment, but if we have insufficient free memory for the
63+
// request this can lead to allocating disparate memory that can violate the
64+
// ARM ABI. Clear free memory so only the new allocations are used, but do
65+
// not release allocated memory as it may still be in-use.
66+
CodeMem.FreeMem.clear();
67+
RODataMem.FreeMem.clear();
68+
RWDataMem.FreeMem.clear();
69+
70+
// Round up to the nearest page size. Blocks must be page-aligned.
71+
RequiredCodeSize = alignTo(RequiredCodeSize, PageSize);
72+
RequiredRODataSize = alignTo(RequiredRODataSize, PageSize);
73+
RequiredRWDataSize = alignTo(RequiredRWDataSize, PageSize);
74+
uint64_t RequiredSize =
75+
RequiredCodeSize + RequiredRODataSize + RequiredRWDataSize;
76+
77+
std::error_code ec;
78+
sys::MemoryBlock MB = MMapper->allocateMappedMemory(
79+
AllocationPurpose::RWData, RequiredSize, nullptr,
80+
sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
81+
if (ec) {
82+
return;
83+
}
84+
// CodeMem will arbitrarily own this MemoryBlock to handle cleanup.
85+
CodeMem.AllocatedMem.push_back(MB);
86+
uintptr_t Addr = (uintptr_t)MB.base();
87+
FreeMemBlock FreeMB;
88+
FreeMB.PendingPrefixIndex = (unsigned)-1;
89+
90+
if (CodeSize > 0) {
91+
assert(isAddrAligned(CodeAlign, (void *)Addr));
92+
FreeMB.Free = sys::MemoryBlock((void *)Addr, RequiredCodeSize);
93+
CodeMem.FreeMem.push_back(FreeMB);
94+
Addr += RequiredCodeSize;
95+
}
96+
97+
if (RODataSize > 0) {
98+
assert(isAddrAligned(RODataAlign, (void *)Addr));
99+
FreeMB.Free = sys::MemoryBlock((void *)Addr, RequiredRODataSize);
100+
RODataMem.FreeMem.push_back(FreeMB);
101+
Addr += RequiredRODataSize;
102+
}
103+
104+
if (RWDataSize > 0) {
105+
assert(isAddrAligned(RWDataAlign, (void *)Addr));
106+
FreeMB.Free = sys::MemoryBlock((void *)Addr, RequiredRWDataSize);
107+
RWDataMem.FreeMem.push_back(FreeMB);
108+
}
109+
}
110+
20111
uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
21112
unsigned Alignment,
22113
unsigned SectionID,
@@ -264,8 +355,10 @@ class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {
264355
};
265356
} // namespace
266357

267-
SectionMemoryManager::SectionMemoryManager(MemoryMapper *UnownedMM)
268-
: MMapper(UnownedMM), OwnedMMapper(nullptr) {
358+
SectionMemoryManager::SectionMemoryManager(MemoryMapper *UnownedMM,
359+
bool ReserveAlloc)
360+
: MMapper(UnownedMM), OwnedMMapper(nullptr),
361+
ReserveAllocation(ReserveAlloc) {
269362
if (!MMapper) {
270363
OwnedMMapper = std::make_unique<DefaultMMapper>();
271364
MMapper = OwnedMMapper.get();

0 commit comments

Comments
 (0)