From 0e6a066126a5bedacc87b400e09b59e59062a1e7 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 23 Feb 2026 22:37:55 +0000 Subject: [PATCH 1/3] Support Linux ARM64 64KB page size without disabling JIT or libpas Linux ARM64 kernels can be configured with 4KB, 16KB, or 64KB page sizes. Previously, the default CeilingOnPageSize for Linux ARM64 was 16KB, which caused an immediate RELEASE_ASSERT crash on systems with 64KB pages (e.g., RHEL aarch64, Oracle Linux, Ubuntu generic-64k). The existing USE(64KB_PAGE_BLOCK) workaround disables both JIT and bmalloc/libpas, which is a significant performance regression. This change raises CeilingOnPageSize to 64KB for all Linux ARM64 builds, and correspondingly updates libpas page/granule sizes to be at least 64KB on Linux ARM64. This allows Bun and other WebKit embedders to run on 64KB page kernels with full JIT and libpas support. Changes: - WTF/PageBlock.h: Set CeilingOnPageSize=64KB for Linux ARM64 - libpas/pas_internal_config.h: Raise small page (16KB->64KB), small bitfit page (16KB->64KB), medium page (128KB->256KB), and granule size (16KB->64KB) for Linux ARM64 - libpas/jit_heap_config.h: Raise JIT small page/granule (16KB->64KB) and JIT medium page/granule (128KB->256KB / 16KB->64KB) - libpas/pas_expendable_memory.h: Raise expendable memory page size (16KB->64KB) for Linux ARM64 - bmalloc/BPlatform.h: Add BUSE_PRECOMPUTED_CONSTANTS_VMPAGE64K These changes propagate correctly through: - ConfigSizeToProtect (via std::max(CeilingOnPageSize, 16*KB)) - OpcodeConfigSizeToProtect (same pattern) - MarkedBlock::blockSize (via std::max(16*KB, CeilingOnPageSize)) - All libpas heap configs that use PAS_*_DEFAULT_SIZE constants - mprotect/mmap calls that use runtime pageSize() Co-Authored-By: Claude --- Source/WTF/wtf/PageBlock.h | 9 ++++---- Source/bmalloc/bmalloc/BPlatform.h | 4 ++++ .../libpas/src/libpas/jit_heap_config.h | 10 ++++++++ .../libpas/src/libpas/pas_expendable_memory.h | 4 ++++ .../libpas/src/libpas/pas_internal_config.h | 23 ++++++++++++++++++- 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/Source/WTF/wtf/PageBlock.h b/Source/WTF/wtf/PageBlock.h index c4f9a8b1a4b3..46a31bf8ab79 100644 --- a/Source/WTF/wtf/PageBlock.h +++ b/Source/WTF/wtf/PageBlock.h @@ -47,13 +47,14 @@ namespace WTF { // On Linux, Power systems normally use 64 KiB pages. // // aarch64 systems seem to be all over the place. Most Linux distros use 4 KiB, but RHEL uses -// 64 KiB. Linux on Apple Silicon uses 16KiB for best performance, so use that for Linux on -// aarch64 by default. USE(64KB_PAGE_BLOCK) allows overriding this. +// 64 KiB. Since we cannot know the page size at compile time on Linux, use 64 KiB as the ceiling +// for Linux ARM64 to support all possible page size configurations (4 KiB, 16 KiB, 64 KiB). +// USE(64KB_PAGE_BLOCK) allows overriding this on other architectures too. // // Use 64 KiB for any unknown CPUs to be conservative. -#if OS(DARWIN) || PLATFORM(PLAYSTATION) || CPU(MIPS) || CPU(MIPS64) || CPU(LOONGARCH64) || (OS(LINUX) && CPU(ARM64) && !USE(64KB_PAGE_BLOCK)) +#if OS(DARWIN) || PLATFORM(PLAYSTATION) || CPU(MIPS) || CPU(MIPS64) || CPU(LOONGARCH64) constexpr size_t CeilingOnPageSize = 16 * KB; -#elif USE(64KB_PAGE_BLOCK) || CPU(PPC) || CPU(PPC64) || CPU(PPC64LE) || CPU(UNKNOWN) +#elif USE(64KB_PAGE_BLOCK) || (OS(LINUX) && CPU(ARM64)) || CPU(PPC) || CPU(PPC64) || CPU(PPC64LE) || CPU(UNKNOWN) constexpr size_t CeilingOnPageSize = 64 * KB; #elif OS(WINDOWS) || CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(ARM64) || CPU(RISCV64) constexpr size_t CeilingOnPageSize = 4 * KB; diff --git a/Source/bmalloc/bmalloc/BPlatform.h b/Source/bmalloc/bmalloc/BPlatform.h index abf75cb0ed38..28eb89f28f0b 100644 --- a/Source/bmalloc/bmalloc/BPlatform.h +++ b/Source/bmalloc/bmalloc/BPlatform.h @@ -454,6 +454,10 @@ #define BUSE_PRECOMPUTED_CONSTANTS_VMPAGE16K 1 #endif +#if !defined(BUSE_PRECOMPUTED_CONSTANTS_VMPAGE64K) +#define BUSE_PRECOMPUTED_CONSTANTS_VMPAGE64K 1 +#endif + /* We only export the mallocSize and mallocGoodSize APIs if they're supported by the SystemHeap allocator (currently only Darwin) and the current bmalloc allocator (currently only libpas). */ #if BUSE(LIBPAS) && BOS(DARWIN) #define BENABLE_MALLOC_SIZE 1 diff --git a/Source/bmalloc/libpas/src/libpas/jit_heap_config.h b/Source/bmalloc/libpas/src/libpas/jit_heap_config.h index 036b1430ae55..88cee8804d73 100644 --- a/Source/bmalloc/libpas/src/libpas/jit_heap_config.h +++ b/Source/bmalloc/libpas/src/libpas/jit_heap_config.h @@ -40,16 +40,26 @@ PAS_BEGIN_EXTERN_C; #define JIT_SMALL_SEGREGATED_MIN_ALIGN (1u << JIT_SMALL_SEGREGATED_MIN_ALIGN_SHIFT) #define JIT_SMALL_BITFIT_MIN_ALIGN_SHIFT 2u #define JIT_SMALL_BITFIT_MIN_ALIGN (1u << JIT_SMALL_BITFIT_MIN_ALIGN_SHIFT) +#if PAS_ARM64 && PAS_OS(LINUX) +#define JIT_SMALL_PAGE_SIZE 65536u +#define JIT_SMALL_GRANULE_SIZE 65536u +#else #define JIT_SMALL_PAGE_SIZE 16384u #define JIT_SMALL_GRANULE_SIZE 16384u +#endif #define JIT_MEDIUM_BITFIT_MIN_ALIGN_SHIFT 8u #define JIT_MEDIUM_BITFIT_MIN_ALIGN (1u << JIT_MEDIUM_BITFIT_MIN_ALIGN_SHIFT) +#if PAS_ARM64 && PAS_OS(LINUX) +#define JIT_MEDIUM_PAGE_SIZE 262144u +#define JIT_MEDIUM_GRANULE_SIZE 65536u +#else #define JIT_MEDIUM_PAGE_SIZE 131072u #if PAS_ARM64 #define JIT_MEDIUM_GRANULE_SIZE 16384u #else #define JIT_MEDIUM_GRANULE_SIZE 4096u #endif +#endif PAS_API void jit_heap_config_activate(void); diff --git a/Source/bmalloc/libpas/src/libpas/pas_expendable_memory.h b/Source/bmalloc/libpas/src/libpas/pas_expendable_memory.h index 737fe9574e0f..5b7418a6d3b6 100644 --- a/Source/bmalloc/libpas/src/libpas/pas_expendable_memory.h +++ b/Source/bmalloc/libpas/src/libpas/pas_expendable_memory.h @@ -51,7 +51,11 @@ struct pas_expendable_memory { pas_expendable_memory_state states[1]; }; +#if PAS_ARM64 && PAS_OS(LINUX) +#define PAS_EXPENDABLE_MEMORY_PAGE_SIZE 65536llu +#else #define PAS_EXPENDABLE_MEMORY_PAGE_SIZE 16384llu +#endif PAS_API extern pas_expendable_memory_state_version pas_expendable_memory_version_counter; diff --git a/Source/bmalloc/libpas/src/libpas/pas_internal_config.h b/Source/bmalloc/libpas/src/libpas/pas_internal_config.h index f8fbb1734d23..1b7dd906f5b9 100644 --- a/Source/bmalloc/libpas/src/libpas/pas_internal_config.h +++ b/Source/bmalloc/libpas/src/libpas/pas_internal_config.h @@ -35,22 +35,43 @@ #define PAS_USE_MARGE_BITFIT_OVERRIDE true /* The OS may have a smaller page size. That's OK. */ +/* On Linux ARM64, kernels may be configured with 64 KiB pages (e.g. RHEL, Oracle Linux). + All page and granule sizes must be at least as large as the system page size, so use + 64 KiB minimums on Linux ARM64 to support all possible kernel page configurations. */ +#if PAS_ARM64 && PAS_OS(LINUX) +#define PAS_SMALL_PAGE_DEFAULT_SHIFT 16 +#else #define PAS_SMALL_PAGE_DEFAULT_SHIFT 14 +#endif #define PAS_SMALL_PAGE_DEFAULT_SIZE ((size_t)1 << PAS_SMALL_PAGE_DEFAULT_SHIFT) +#if PAS_ARM64 && PAS_OS(LINUX) +#define PAS_SMALL_BITFIT_PAGE_DEFAULT_SHIFT 16 +#else #define PAS_SMALL_BITFIT_PAGE_DEFAULT_SHIFT 14 +#endif #define PAS_SMALL_BITFIT_PAGE_DEFAULT_SIZE ((size_t)1 << PAS_SMALL_BITFIT_PAGE_DEFAULT_SHIFT) +#if PAS_ARM64 && PAS_OS(LINUX) +#define PAS_MEDIUM_PAGE_DEFAULT_SHIFT 18 +#else #define PAS_MEDIUM_PAGE_DEFAULT_SHIFT 17 +#endif #define PAS_MEDIUM_PAGE_DEFAULT_SIZE ((size_t)1 << PAS_MEDIUM_PAGE_DEFAULT_SHIFT) +#if PAS_ARM64 && PAS_OS(LINUX) +#define PAS_MEDIUM_BITFIT_PAGE_DEFAULT_SHIFT 19 +#else #define PAS_MEDIUM_BITFIT_PAGE_DEFAULT_SHIFT 19 +#endif #define PAS_MEDIUM_BITFIT_PAGE_DEFAULT_SIZE ((size_t)1 << PAS_MEDIUM_BITFIT_PAGE_DEFAULT_SHIFT) #define PAS_MARGE_PAGE_DEFAULT_SHIFT 22 #define PAS_MARGE_PAGE_DEFAULT_SIZE ((size_t)1 << PAS_MARGE_PAGE_DEFAULT_SHIFT) -#if PAS_ARM64 || PAS_PLATFORM(PLAYSTATION) +#if PAS_ARM64 && PAS_OS(LINUX) +#define PAS_GRANULE_DEFAULT_SHIFT 16 +#elif PAS_ARM64 || PAS_PLATFORM(PLAYSTATION) #define PAS_GRANULE_DEFAULT_SHIFT 14 #else #define PAS_GRANULE_DEFAULT_SHIFT 12 From 82cc186387866859beff40282f679c6d76baf0a2 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Mon, 9 Mar 2026 18:19:21 -0700 Subject: [PATCH 2/3] Derive PAS_MAX_OBJECTS_PER_PAGE from small page size The previous hardcoded 2048 assumed PAS_SMALL_PAGE_DEFAULT_SIZE=16KB. With 64KB pages, num_alloc_bits = page_size >> min_align_shift can reach 8192 (utility heap, PAS_INTERNAL_MIN_ALIGN_SHIFT=3), causing pas_baseline_allocator_attach_directory to PAS_ASSERT when a thread hits the baseline allocator slow path. --- Source/bmalloc/libpas/src/libpas/pas_internal_config.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/bmalloc/libpas/src/libpas/pas_internal_config.h b/Source/bmalloc/libpas/src/libpas/pas_internal_config.h index 1b7dd906f5b9..16a63b28516f 100644 --- a/Source/bmalloc/libpas/src/libpas/pas_internal_config.h +++ b/Source/bmalloc/libpas/src/libpas/pas_internal_config.h @@ -152,7 +152,9 @@ #define PAS_NUM_BASELINE_ALLOCATORS 32u -#define PAS_MAX_OBJECTS_PER_PAGE 2048 +/* Must be >= max(page_size >> min_align_shift) across all segregated configs. + Utility heap uses PAS_INTERNAL_MIN_ALIGN_SHIFT (3) with the small page. */ +#define PAS_MAX_OBJECTS_PER_PAGE (PAS_SMALL_PAGE_DEFAULT_SIZE >> PAS_INTERNAL_MIN_ALIGN_SHIFT) #define PAS_MPROTECT_DECOMMITTED PAS_ENABLE_TESTING From d56ecd41668da3a706ca066d7d5d341de472a2a7 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Tue, 17 Mar 2026 21:15:40 +0000 Subject: [PATCH 3/3] Bump JIT_MEDIUM_BITFIT_MIN_ALIGN_SHIFT to 9 on Linux ARM64 to keep granule use-count under uint8_t DECOMMITTED sentinel --- Source/bmalloc/libpas/src/libpas/jit_heap_config.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/bmalloc/libpas/src/libpas/jit_heap_config.h b/Source/bmalloc/libpas/src/libpas/jit_heap_config.h index 88cee8804d73..78c21cc2f7e0 100644 --- a/Source/bmalloc/libpas/src/libpas/jit_heap_config.h +++ b/Source/bmalloc/libpas/src/libpas/jit_heap_config.h @@ -47,7 +47,13 @@ PAS_BEGIN_EXTERN_C; #define JIT_SMALL_PAGE_SIZE 16384u #define JIT_SMALL_GRANULE_SIZE 16384u #endif +#if PAS_ARM64 && PAS_OS(LINUX) +/* granule_size >> min_align_shift must be < 254 (use_count is uint8_t with 255 = DECOMMITTED). + 64K >> 8 = 256 overflows; 64K >> 9 = 128 is safe. Matches PAS_MIN_MEDIUM_ALIGN_SHIFT. */ +#define JIT_MEDIUM_BITFIT_MIN_ALIGN_SHIFT 9u +#else #define JIT_MEDIUM_BITFIT_MIN_ALIGN_SHIFT 8u +#endif #define JIT_MEDIUM_BITFIT_MIN_ALIGN (1u << JIT_MEDIUM_BITFIT_MIN_ALIGN_SHIFT) #if PAS_ARM64 && PAS_OS(LINUX) #define JIT_MEDIUM_PAGE_SIZE 262144u