2 Commits

Author SHA1 Message Date
Raúl Marín
82e379b8f2 Address review: block-comment style and document the macOS 12+ requirement
Reformat the malloc_getcpu Apple comment to the block style and note that the
tpidr_el0/sidt scheme requires macOS 12+: macOS 11 and earlier kept the CPU
number in tpidrro_el0's low 3 bits (now retired). Cite xnu's
__TPIDR_CPU_NUM_MASK / MACHDEP_TPIDR_CPUNUM_MASK, kept in sync with
_os_cpu_number, as the source of truth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:21:17 -07:00
Raúl Marín
5aabbc87bf Fix malloc_getcpu on macOS to read the current CPU number correctly
The Apple branch read the CPU number from the low 3 bits of `tpidrro_el0`.
That layout no longer holds on Apple Silicon: those bits read back as 0, so
`malloc_getcpu` returned 0 for every thread. `percpu_arena` then funneled all
allocations into arena 0, or was disabled outright at init.

Read the CPU number the same way as libplatform's `_os_cpu_number`: the low 12
bits of `tpidr_el0` on arm64, or of the IDT base (via `sidt`) on x86. Also move
the Apple branch ahead of the `rdtscp` one so Apple x86 uses `sidt` rather than
`rdtscp`, whose `ecx` is not the CPU id under XNU.
https://github.com/apple-oss-distributions/xnu/blob/main/libsyscall/os/tsd.h

Verified on a 12-CPU M2 Pro: with `percpu_arena:percpu`, `opt.percpu_arena` now
stays `percpu` and `thread.arena` spreads across all 12 arenas {0..11}; the old
code collapsed every thread onto arena 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:21:17 -07:00

View File

@@ -18,15 +18,34 @@ malloc_getcpu(void) {
return GetCurrentProcessorNumber();
#elif defined(JEMALLOC_HAVE_SCHED_GETCPU)
return (malloc_cpuid_t)sched_getcpu();
#elif defined(__APPLE__)
/*
* No sched_getcpu() on macOS; read the CPU number like _os_cpu_number()
* does, from the low 12 bits of tpidr_el0 (arm64) or the IDT base (x86).
* The 0xfff mask is xnu's __TPIDR_CPU_NUM_MASK, kept in sync with
* _os_cpu_number in libsyscall/os/tsd.h (the kernel counterpart is
* MACHDEP_TPIDR_CPUNUM_MASK in osfmk/arm64/machine_machdep.h):
* https://github.com/apple-oss-distributions/xnu/blob/main/libsyscall/os/tsd.h
* This requires macOS 12+: on macOS 11 and earlier arm64 kept the CPU
* number in tpidrro_el0's low 3 bits instead, so it would be misread here.
* Those releases are retired by Apple, so only the current layout is handled.
*/
# if defined(__aarch64__)
uint64_t cpu;
__asm__ __volatile__("mrs %0, tpidr_el0" : "=r"(cpu));
return (malloc_cpuid_t)(cpu & 0xfff);
# elif defined(__x86_64__) || defined(__i386__)
struct { uintptr_t p1, p2; } idtr;
__asm__ __volatile__("sidt %0" : "=m"(idtr));
return (malloc_cpuid_t)(idtr.p1 & 0xfff);
# else
not_reached();
return -1;
# endif
#elif defined(JEMALLOC_HAVE_RDTSCP)
unsigned int ecx;
asm volatile("rdtscp" : "=c"(ecx)::"eax", "edx");
return (malloc_cpuid_t)(ecx & 0xfff);
#elif defined(__aarch64__) && defined(__APPLE__)
/* Other oses most likely use tpidr_el0 instead */
uintptr_t c;
asm volatile("mrs %x0, tpidrro_el0" : "=r"(c)::"memory");
return (malloc_cpuid_t)(c & (1 << 3) - 1);
#else
not_reached();
return -1;