summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2022-02-06 10:29:27 -0500
committerBen Gamari <ben@smart-cactus.org>2022-02-09 18:35:01 -0500
commit8cc36ace847a55388b7118b82623a6a57a6ad44f (patch)
tree4f115dac8a027826a4bfab7dde9db570ccf75d25
parent20d9dbbef58aecbf6db95beca8c8e1ce817c5214 (diff)
downloadhaskell-8cc36ace847a55388b7118b82623a6a57a6ad44f.tar.gz
rts/PEi386: Avoid accidentally-quadratic allocation cost
We now preserve the address that we last mapped, allowing us to resume our search and avoiding quadratic allocation costs. This fixes the runtime of T10296a, which allocates many adjustors.
-rw-r--r--rts/linker/MMap.c64
1 files changed, 45 insertions, 19 deletions
diff --git a/rts/linker/MMap.c b/rts/linker/MMap.c
index c506100816..2d6c2aa148 100644
--- a/rts/linker/MMap.c
+++ b/rts/linker/MMap.c
@@ -92,7 +92,7 @@ static int virtualQuery(void *baseAddr, PMEMORY_BASIC_INFORMATION info)
static inline uintptr_t round_up(uintptr_t num, uint64_t factor)
{
- return num + factor - 1 - (num + factor - 1) % factor;
+ return num + factor - 1 - (num + factor - 1) % factor;
}
/*
@@ -101,30 +101,22 @@ static inline uintptr_t round_up(uintptr_t num, uint64_t factor)
* bytes you *must* allocate is returned in REQ. You are free to use less but
* you must allocate the amount given in REQ. If not successful NULL.
*/
-static void *allocateBytes(void* baseAddr, size_t sz, size_t *req)
+static void *allocateBytes(void* baseAddr, void *endAddr, size_t sz, size_t *req)
{
SYSTEM_INFO sys;
GetSystemInfo(&sys);
- const uint64_t max_range = 4294967296UL;
- IF_DEBUG(linker_verbose, debugBelch("Base Address 0x%p\n", baseAddr));
- IF_DEBUG(linker_verbose, debugBelch("Requesting mapping of %" FMT_SizeT " bytes within range %"
- PRId64 " bytes\n", sz, max_range));
- MEMORY_BASIC_INFORMATION info;
- IF_DEBUG(linker_verbose, debugBelch("Initial query @ 0x%p...\n", baseAddr));
- int res = virtualQuery(baseAddr, &info);
- if (res) {
- return NULL;
- }
+ IF_DEBUG(linker_verbose, debugBelch("Requesting mapping of %" FMT_SizeT " bytes between %p and %p\n",
+ sz, baseAddr, endAddr));
- uint8_t *endAddr = (uint8_t *) baseAddr + max_range;
- uint8_t *initialAddr = info.AllocationBase;
+ MEMORY_BASIC_INFORMATION info;
+ uint8_t *initialAddr = baseAddr;
uint8_t *region = NULL;
while (!region
- && (uint64_t) llabs(initialAddr - endAddr) <= max_range
+ && initialAddr <= (uint8_t *) endAddr
&& (void *) initialAddr < sys.lpMaximumApplicationAddress)
{
- res = virtualQuery(initialAddr, &info);
+ int res = virtualQuery(initialAddr, &info);
if (res) {
return NULL;
}
@@ -133,8 +125,6 @@ static void *allocateBytes(void* baseAddr, size_t sz, size_t *req)
IF_DEBUG(linker_verbose, debugBelch("Free range at 0x%p of %zu bytes\n",
info.BaseAddress, info.RegionSize));
- MEMORY_BASIC_INFORMATION info2;
- res = virtualQuery(endAddr+1, &info2);
if (info.RegionSize >= sz) {
if (info.AllocationBase == 0) {
size_t needed_sz = round_up (sz, sys.dwAllocationGranularity);
@@ -163,6 +153,42 @@ static void *allocateBytes(void* baseAddr, size_t sz, size_t *req)
return region;
}
+/* Find free address space for mapping anonymous memory. */
+static void *allocateLocalBytes(size_t sz, size_t *req)
+{
+ // We currently don't attempt to take address space from the region below
+ // the image as malloc() tends to like to use this space, but we could do if
+ // necessary.
+ size_t max_range = 0x7fffffff - sz;
+
+ static void *base_addr = NULL;
+ if (base_addr == NULL) {
+ base_addr = GetModuleHandleW(NULL);
+ }
+ uint8_t *end_addr = (uint8_t *) base_addr + max_range;
+
+ // We track the location of the last allocation to avoid having to
+ // do a linear search of address space looking for space on every allocation
+ // as this can easily devolve into quadratic complexity.
+ static void *last_alloca = NULL;
+ if (last_alloca == NULL) {
+ // Start the search at the image base
+ last_alloca = base_addr;
+ }
+
+ void *result = NULL;
+ result = allocateBytes (last_alloca, end_addr, sz, req);
+ if (result == NULL) {
+ // We failed to find suitable address space; restart the search at base_addr.
+ result = allocateBytes (base_addr, end_addr, sz, req);
+ }
+
+ if (result != NULL) {
+ last_alloca = (uint8_t *) result + *req;
+ }
+ return result;
+}
+
static DWORD
memoryAccessToProt(MemoryAccess access)
{
@@ -187,7 +213,7 @@ mmapAnonForLinker (size_t bytes)
/* For linking purposes we want to load code within a 4GB range from the
load address of the application. As such we need to find a location to
allocate at. */
- void* region = allocateBytes (GetModuleHandleW (NULL), bytes, &size);
+ void* region = allocateLocalBytes (bytes, &size);
if (region == NULL) {
return NULL;
}