From a407780915469a7352b2ca8b0b6d698b0d82bd3f Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 20 Sep 2024 22:05:28 -0400 Subject: [PATCH 1/3] CMakeLists.txt: check /proc/cpuinfo for an SV39 MMU If the host has an SV39 MMU, the usual aligned hinting strategy will not work despite the system being 64-bit. On RISC-V linux systems, the type of MMU can be read from /proc/cpuinfo. If we find one, a constant is defined that will allow us to skip the aligned hinting when the time comes. --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bcfe91d86..20b22c090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -343,6 +343,16 @@ if(MINGW) add_definitions(-D_WIN32_WINNT=0x600) endif() +# Check /proc/cpuinfo for an SV39 MMU and define a constant if one is +# found. We will want to skip the aligned hinting in that case. +if (EXISTS /proc/cpuinfo) + file(STRINGS /proc/cpuinfo mi_sv39_mmu REGEX "^mmu[ \t]+:[ \t]+sv39$") + if (mi_sv39_mmu) + MESSAGE( STATUS "SV39 MMU detected" ) + list(APPEND mi_defines MI_SV39_MMU=1) + endif() +endif() + # extra needed libraries # we prefer -l test over `find_library` as sometimes core libraries From 53440ad69a803dca06b8dcfd3ce332e36631d48b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 20 Sep 2024 22:10:22 -0400 Subject: [PATCH 2/3] src/os.c: skip aligned hinting when MI_SV39_MMU=1 is defined This constant will be defined when the host has an SV39 MMU. The usual 2TiB+ hinting strategy will not work on those systems, so we skip it, just as we would if the system was not "64-bit." --- src/os.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/os.c b/src/os.c index ce104273b..f5f1ef283 100644 --- a/src/os.c +++ b/src/os.c @@ -77,8 +77,10 @@ bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats -------------------------------------------------------------- */ // On 64-bit systems, we can do efficient aligned allocation by using -// the 2TiB to 30TiB area to allocate those. -#if (MI_INTPTR_SIZE >= 8) +// the 2TiB to 30TiB area to allocate those. The one exception is on +// 64-bit RISC-V systems that have an SV39 MMU; there the 256GiB+ +// range (and specifically the 2TiB+ range) will not be usable. +#if (MI_INTPTR_SIZE >= 8) && (!defined(MI_SV39_MMU) || MI_SV39_MMU == 0) static mi_decl_cache_align _Atomic(uintptr_t)aligned_base; // Return a MI_SEGMENT_SIZE aligned address that is probably available. From 4f1a42022c317365390e0c3e18c02b4351ce26aa Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 18 Oct 2024 09:33:19 -0400 Subject: [PATCH 3/3] src/os.c: avoid doomed alloc/free on 32-bit and SV39 systems On 32-bit or SV39 systems, _mi_os_get_aligned_hint() in src/os.c is ifdef'd to always return NULL. As a result, the OS prim allocation routines using that hint will fail to obtain aligned memory. When that happens, we free() the result, and overallocate. But this is wasteful: we know that (with high probability) the first attempt at an aligned allocation will fail. This commit modifies mi_os_prim_alloc_aligned() to skip the doomed alloc and subsequent free using the same ifdef condition that is used to disable the hint. --- src/os.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/os.c b/src/os.c index f5f1ef283..fa727f744 100644 --- a/src/os.c +++ b/src/os.c @@ -215,6 +215,10 @@ static void* mi_os_prim_alloc(size_t size, size_t try_alignment, bool commit, bo // Primitive aligned allocation from the OS. // This function guarantees the allocated memory is aligned. static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** base, mi_stats_t* stats) { + #if (MI_INTPTR_SIZE < 8) || (defined(MI_SV39_MMU) && MI_SV39_MMU != 0) + MI_UNUSED(allow_large); // not used on 32-bit/SV39 systems + #endif + mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0)); mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); mi_assert_internal(is_large != NULL); @@ -224,18 +228,33 @@ static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL; size = _mi_align_up(size, _mi_os_page_size()); - // try first with a hint (this will be aligned directly on Win 10+ or BSD) - void* p = mi_os_prim_alloc(size, alignment, commit, allow_large, is_large, is_zero, stats); + void* p = NULL; + + #if (MI_INTPTR_SIZE >= 8) && (!defined(MI_SV39_MMU) || MI_SV39_MMU == 0) + // Try first with a hint. This will be usually be aligned directly + // on Win 10+ or BSD, but if this is a 32-bit or SV39 system, we + // don't bother -- it's not going to work. + p = mi_os_prim_alloc(size, alignment, commit, allow_large, is_large, is_zero, stats); if (p == NULL) return NULL; + #endif // aligned already? - if (((uintptr_t)p % alignment) == 0) { + if (p && ((uintptr_t)p % alignment) == 0) { *base = p; } else { - // if not aligned, free it, overallocate, and unmap around it + // If p is not aligned, then either we tried and failed to obtain + // an aligned allocation, or we didn't try at all. Only one of + // these is a warning scenario. + #if (MI_INTPTR_SIZE >= 8) && (!defined(MI_SV39_MMU) || MI_SV39_MMU == 0) + // Skip the warning and the free() if we never attempted an aligned + // allocation in the first place. _mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (size: 0x%zx bytes, address: %p, alignment: 0x%zx, commit: %d)\n", size, p, alignment, commit); mi_os_prim_free(p, size, commit, stats); + #endif + + // now overallocate + if (size >= (SIZE_MAX - alignment)) return NULL; // overflow const size_t over_size = size + alignment;