| |
| |
| |
| |
| |
| |
| #pragma once |
| #ifndef MIMALLOC_PRIM_H |
| #define MIMALLOC_PRIM_H |
| #include "internal.h" |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| typedef struct mi_os_mem_config_s { |
| size_t page_size; |
| size_t large_page_size; |
| size_t alloc_granularity; |
| size_t physical_memory_in_kib; |
| size_t virtual_address_bits; |
| bool has_overcommit; |
| bool has_partial_free; |
| bool has_virtual_reserve; |
| bool has_transparent_huge_pages; |
| } mi_os_mem_config_t; |
|
|
| |
| void _mi_prim_mem_init( mi_os_mem_config_t* config ); |
|
|
| |
| int _mi_prim_free(void* addr, size_t size ); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr); |
|
|
| |
| |
| |
| int _mi_prim_commit(void* addr, size_t size, bool* is_zero); |
|
|
| |
| |
| |
| |
| int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit); |
|
|
| |
| |
| int _mi_prim_reset(void* addr, size_t size); |
|
|
| |
| |
| |
| int _mi_prim_reuse(void* addr, size_t size); |
|
|
| |
| int _mi_prim_protect(void* addr, size_t size, bool protect); |
|
|
| |
| |
| |
| |
| int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr); |
|
|
| |
| size_t _mi_prim_numa_node(void); |
|
|
| |
| size_t _mi_prim_numa_node_count(void); |
|
|
| |
| mi_msecs_t _mi_prim_clock_now(void); |
|
|
| |
| typedef struct mi_process_info_s { |
| mi_msecs_t elapsed; |
| mi_msecs_t utime; |
| mi_msecs_t stime; |
| size_t current_rss; |
| size_t peak_rss; |
| size_t current_commit; |
| size_t peak_commit; |
| size_t page_faults; |
| } mi_process_info_t; |
|
|
| void _mi_prim_process_info(mi_process_info_t* pinfo); |
|
|
| |
| |
| void _mi_prim_out_stderr( const char* msg ); |
|
|
| |
| |
| bool _mi_prim_getenv(const char* name, char* result, size_t result_size); |
|
|
|
|
| |
| |
| bool _mi_prim_random_buf(void* buf, size_t buf_len); |
|
|
| |
| void _mi_prim_thread_init_auto_done(void); |
|
|
| |
| void _mi_prim_thread_done_auto_done(void); |
|
|
| |
| void _mi_prim_thread_associate_default_theap(mi_theap_t* theap); |
|
|
| |
| bool _mi_prim_thread_is_in_threadpool(void); |
|
|
| |
| |
| void _mi_prim_thread_yield(void); |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| #if (defined(_WIN32)) || \ |
| (defined(__GNUC__) && ( \ |
| (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__))) \ |
| || (defined(__APPLE__) && (defined(__x86_64__) || defined(__aarch64__) || defined(__POWERPC__))) \ |
| || (defined(__BIONIC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__))) \ |
| || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ |
| || (defined(__OpenBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ |
| )) |
|
|
| static inline void* mi_prim_tls_slot(size_t slot) mi_attr_noexcept { |
| void* res; |
| const size_t ofs = (slot*sizeof(void*)); |
| #if defined(_WIN32) |
| #if (_M_X64 || _M_AMD64) && !defined(_M_ARM64EC) |
| res = (void*)__readgsqword((unsigned long)ofs); |
| #elif _M_IX86 && !defined(_M_ARM64EC) |
| res = (void*)__readfsdword((unsigned long)ofs); |
| #else |
| res = ((void**)NtCurrentTeb())[slot]; MI_UNUSED(ofs); |
| #endif |
| #elif defined(__i386__) |
| __asm__("movl %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); |
| #elif defined(__APPLE__) && defined(__x86_64__) |
| __asm__("movq %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); |
| #elif defined(__x86_64__) && (MI_INTPTR_SIZE==4) |
| __asm__("movl %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); |
| #elif defined(__x86_64__) |
| __asm__("movq %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); |
| #elif defined(__arm__) |
| void** tcb; MI_UNUSED(ofs); |
| __asm__ volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb)); |
| res = tcb[slot]; |
| #elif defined(__aarch64__) |
| void** tcb; MI_UNUSED(ofs); |
| #if defined(__APPLE__) |
| __asm__ volatile ("mrs %0, tpidrro_el0\nbic %0, %0, #7" : "=r" (tcb)); |
| #else |
| __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); |
| #endif |
| res = tcb[slot]; |
| #elif defined(__APPLE__) && defined(__POWERPC__) |
| MI_UNUSED(ofs); |
| res = pthread_getspecific(slot); |
| #else |
| #define MI_HAS_TLS_SLOT 0 |
| MI_UNUSED(ofs); |
| res = NULL; |
| #endif |
| return res; |
| } |
|
|
| #ifndef MI_HAS_TLS_SLOT |
| #define MI_HAS_TLS_SLOT 1 |
| #endif |
|
|
| |
| static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexcept { |
| const size_t ofs = (slot*sizeof(void*)); |
| #if defined(_WIN32) |
| ((void**)NtCurrentTeb())[slot] = value; MI_UNUSED(ofs); |
| #elif defined(__i386__) |
| __asm__("movl %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); |
| #elif defined(__APPLE__) && defined(__x86_64__) |
| __asm__("movq %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); |
| #elif defined(__x86_64__) && (MI_INTPTR_SIZE==4) |
| __asm__("movl %1,%%fs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); |
| #elif defined(__x86_64__) |
| __asm__("movq %1,%%fs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); |
| #elif defined(__arm__) |
| void** tcb; MI_UNUSED(ofs); |
| __asm__ volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb)); |
| tcb[slot] = value; |
| #elif defined(__aarch64__) |
| void** tcb; MI_UNUSED(ofs); |
| #if defined(__APPLE__) |
| __asm__ volatile ("mrs %0, tpidrro_el0\nbic %0, %0, #7" : "=r" (tcb)); |
| #else |
| __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); |
| #endif |
| tcb[slot] = value; |
| #elif defined(__APPLE__) && defined(__POWERPC__) |
| MI_UNUSED(ofs); |
| pthread_setspecific(slot, value); |
| #else |
| MI_UNUSED(ofs); MI_UNUSED(value); |
| #endif |
| } |
|
|
| #endif |
|
|
|
|
| |
| extern mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_main; |
| extern mi_decl_hidden bool _mi_process_is_initialized; |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| |
| |
| |
| |
| #if !defined(MI_USE_BUILTIN_THREAD_POINTER) |
| #if !defined(__APPLE__) \ |
| && !defined(__CYGWIN__) \ |
| && !defined(MI_LIBC_MUSL) \ |
| && (!defined(__clang_major__) || __clang_major__ >= 14) |
| #if (defined(__GNUC__) && (__GNUC__ >= 7) && defined(__aarch64__)) \ |
| || (defined(__GNUC__) && (__GNUC__ >= 11) && defined(__x86_64__)) \ |
| || (defined(__clang_major__) && (__clang_major__ >= 14) && (defined(__aarch64__) || defined(__x86_64__))) |
| #define MI_USE_BUILTIN_THREAD_POINTER 1 |
| #endif |
| #endif |
| #endif |
|
|
| static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept; |
|
|
| static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { |
| const mi_threadid_t tid = __mi_prim_thread_id(); |
| mi_assert_internal(tid > 1); |
| mi_assert_internal((tid & MI_PAGE_FLAG_MASK) == 0); |
| return tid; |
| } |
|
|
| |
| #if defined(MI_PRIM_THREAD_ID) |
|
|
| static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { |
| return MI_PRIM_THREAD_ID(); |
| } |
|
|
| #elif defined(_WIN32) |
|
|
| static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { |
| |
| return (uintptr_t)NtCurrentTeb(); |
| } |
|
|
| #elif MI_USE_BUILTIN_THREAD_POINTER |
|
|
| static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { |
| |
| return (uintptr_t)__builtin_thread_pointer(); |
| } |
|
|
| #elif MI_HAS_TLS_SLOT |
|
|
| static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { |
| #if defined(__BIONIC__) |
| |
| |
| return (uintptr_t)mi_prim_tls_slot(1); |
| #else |
| |
| |
| |
| return (uintptr_t)mi_prim_tls_slot(0); |
| #endif |
| } |
|
|
| #else |
|
|
| |
| static inline mi_threadid_t __mi_prim_thread_id(void) mi_attr_noexcept { |
| return (uintptr_t)&__mi_theap_main; |
| } |
|
|
| #endif |
|
|
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| static inline mi_theap_t* _mi_theap_default(void); |
| static inline mi_theap_t* _mi_theap_cached(void); |
|
|
| #if defined(_WIN32) |
| #define MI_TLS_MODEL_DYNAMIC_WIN32 1 |
| #elif defined(__APPLE__) && MI_HAS_TLS_SLOT && !defined(__POWERPC__) |
| |
| #define MI_TLS_MODEL_FIXED_SLOT 1 |
| #define MI_TLS_MODEL_FIXED_SLOT_DEFAULT 108 |
| #define MI_TLS_MODEL_FIXED_SLOT_CACHED 109 |
| |
| |
| #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__ANDROID__) |
| #define MI_TLS_MODEL_DYNAMIC_PTHREADS 1 |
| |
| #else |
| #define MI_TLS_MODEL_THREAD_LOCAL 1 |
| #endif |
|
|
| |
| mi_decl_cold mi_decl_noinline mi_theap_t* _mi_theap_empty_get(void); |
|
|
| static inline mi_theap_t* __mi_theap_empty(void) { |
| #if __GNUC__ |
| __asm(""); |
| return (mi_theap_t*)&_mi_theap_empty; |
| #else |
| return _mi_theap_empty_get(); |
| #endif |
| } |
|
|
| #if MI_TLS_MODEL_THREAD_LOCAL |
| |
|
|
| extern mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_default; |
| extern mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_cached; |
|
|
| static inline mi_theap_t* _mi_theap_default(void) { |
| #if defined(MI_TLS_RECURSE_GUARD) |
| if (mi_unlikely(!_mi_process_is_initialized)) return _mi_theap_empty_get(); |
| #endif |
| return __mi_theap_default; |
| } |
|
|
| static inline mi_theap_t* _mi_theap_cached(void) { |
| return __mi_theap_cached; |
| } |
|
|
| #elif MI_TLS_MODEL_FIXED_SLOT |
| |
| #define MI_THEAP_INITASNULL 1 |
|
|
| static inline mi_theap_t* _mi_theap_default(void) { |
| return (mi_theap_t*)mi_prim_tls_slot(MI_TLS_MODEL_FIXED_SLOT_DEFAULT); |
| } |
|
|
| static inline mi_theap_t* _mi_theap_cached(void) { |
| return (mi_theap_t*)mi_prim_tls_slot(MI_TLS_MODEL_FIXED_SLOT_CACHED); |
| } |
|
|
| #elif MI_TLS_MODEL_DYNAMIC_WIN32 |
| |
| #define MI_THEAP_INITASNULL 1 |
|
|
| |
| |
| #if MI_SIZE_SIZE==4 |
| #define MI_TLS_EXPANSION_SLOT (0x0F94 / MI_SIZE_SIZE) |
| #else |
| #define MI_TLS_EXPANSION_SLOT (0x1780 / MI_SIZE_SIZE) |
| #endif |
|
|
| extern mi_decl_hidden size_t _mi_theap_default_slot; |
| extern mi_decl_hidden size_t _mi_theap_cached_slot; |
| extern mi_decl_hidden size_t _mi_theap_default_expansion_slot; |
| extern mi_decl_hidden size_t _mi_theap_cached_expansion_slot; |
|
|
| static inline mi_theap_t* _mi_theap_default(void) { |
| const size_t slot = _mi_theap_default_slot; |
| mi_theap_t* theap = (mi_theap_t*)mi_prim_tls_slot(slot); |
| #if !MI_WIN_DIRECT_TLS |
| if mi_unlikely(slot==MI_TLS_EXPANSION_SLOT) { |
| if mi_likely(theap!=NULL) { |
| theap = ((mi_theap_t**)theap)[_mi_theap_default_expansion_slot]; |
| } |
| } |
| #endif |
| return theap; |
| } |
|
|
| static inline mi_theap_t* _mi_theap_cached(void) { |
| const size_t slot = _mi_theap_cached_slot; |
| mi_theap_t* theap = (mi_theap_t*)mi_prim_tls_slot(slot); |
| #if !MI_WIN_DIRECT_TLS |
| if mi_unlikely(slot==MI_TLS_EXPANSION_SLOT) { |
| if mi_likely(theap!=NULL) { |
| theap = ((mi_theap_t**)theap)[_mi_theap_cached_expansion_slot]; |
| } |
| } |
| #endif |
| return theap; |
| } |
|
|
| #elif MI_TLS_MODEL_DYNAMIC_PTHREADS |
| |
| #define MI_THEAP_INITASNULL 1 |
|
|
| extern mi_decl_hidden pthread_key_t _mi_theap_default_key; |
| extern mi_decl_hidden pthread_key_t _mi_theap_cached_key; |
|
|
| static inline mi_theap_t* _mi_theap_default(void) { |
| #if !MI_TLS_MODEL_DYNAMIC_PTHREADS_DEFAULT_ENTRY_IS_NULL |
| |
| if mi_unlikely(_mi_theap_default_key==0) { return NULL; } |
| #endif |
| return (mi_theap_t*)pthread_getspecific(_mi_theap_default_key); |
| } |
|
|
| static inline mi_theap_t* _mi_theap_cached(void) { |
| #if !MI_TLS_MODEL_DYNAMIC_PTHREADS_DEFAULT_ENTRY_IS_NULL |
| |
| if mi_unlikely(_mi_theap_cached_key==0) { return NULL; } |
| #endif |
| return (mi_theap_t*)pthread_getspecific(_mi_theap_cached_key); |
| } |
|
|
| #else |
| #error "no TLS model is defined for this platform?" |
| #endif |
|
|
|
|
| |
| static inline bool _mi_thread_is_initialized(void) { |
| return (mi_theap_is_initialized(_mi_theap_default())); |
| } |
|
|
| |
| |
| static inline mi_theap_t* _mi_heap_theap(const mi_heap_t* heap) { |
| mi_theap_t* theap = _mi_theap_cached(); |
| #if MI_THEAP_INITASNULL |
| if mi_likely(theap!=NULL && _mi_theap_heap(theap)==heap) return theap; |
| #else |
| if mi_likely(_mi_theap_heap(theap)==heap) return theap; |
| #endif |
| return _mi_heap_theap_get_or_init(heap); |
| } |
|
|
| |
| static inline mi_theap_t* _mi_heap_theap_peek(const mi_heap_t* heap) { |
| mi_theap_t* theap = _mi_theap_cached(); |
| #if MI_THEAP_INITASNULL |
| if mi_unlikely(theap==NULL || _mi_theap_heap(theap)!=heap) |
| #else |
| if mi_unlikely(_mi_theap_heap(theap)!=heap) |
| #endif |
| { |
| theap = _mi_heap_theap_get_peek(heap); |
| } |
| mi_assert(theap==NULL || _mi_theap_heap(theap)==heap); |
| return theap; |
| } |
|
|
| |
| |
| static inline mi_theap_t* _mi_page_associated_theap_peek(mi_page_t* page) { |
| mi_heap_t* const heap = page->heap; |
| mi_theap_t* theap; |
| if mi_likely(heap==NULL) { theap = __mi_theap_main; } |
| else { theap = _mi_heap_theap_peek(heap); } |
| mi_assert_internal(theap==NULL || _mi_thread_id()==theap->tld->thread_id); |
| return theap; |
| } |
|
|
| #endif |
|
|