Path: blob/main/system/lib/mimalloc/src/segment-map.c
6175 views
/* ----------------------------------------------------------------------------1Copyright (c) 2019-2023, Microsoft Research, Daan Leijen2This is free software; you can redistribute it and/or modify it under the3terms of the MIT license. A copy of the license can be found in the file4"LICENSE" at the root of this distribution.5-----------------------------------------------------------------------------*/67/* -----------------------------------------------------------8The following functions are to reliably find the segment or9block that encompasses any pointer p (or NULL if it is not10in any of our segments).11We maintain a bitmap of all memory with 1 bit per MI_SEGMENT_SIZE (64MiB)12set to 1 if it contains the segment meta data.13----------------------------------------------------------- */14#include "mimalloc.h"15#include "mimalloc/internal.h"16#include "mimalloc/atomic.h"1718#if (MI_INTPTR_SIZE>=8) && MI_TRACK_ASAN19#define MI_MAX_ADDRESS ((size_t)140 << 40) // 140TB (see issue #881)20#elif (MI_INTPTR_SIZE >= 8)21#define MI_MAX_ADDRESS ((size_t)40 << 40) // 40TB (to include huge page areas)22#else23#define MI_MAX_ADDRESS ((size_t)2 << 30) // 2Gb24#endif2526#define MI_SEGMENT_MAP_BITS (MI_MAX_ADDRESS / MI_SEGMENT_SIZE)27#define MI_SEGMENT_MAP_SIZE (MI_SEGMENT_MAP_BITS / 8)28#define MI_SEGMENT_MAP_WSIZE (MI_SEGMENT_MAP_SIZE / MI_INTPTR_SIZE)2930static _Atomic(uintptr_t) mi_segment_map[MI_SEGMENT_MAP_WSIZE + 1]; // 2KiB per TB with 64MiB segments3132static size_t mi_segment_map_index_of(const mi_segment_t* segment, size_t* bitidx) {33// note: segment can be invalid or NULL.34mi_assert_internal(_mi_ptr_segment(segment + 1) == segment); // is it aligned on MI_SEGMENT_SIZE?35if ((uintptr_t)segment >= MI_MAX_ADDRESS) {36*bitidx = 0;37return MI_SEGMENT_MAP_WSIZE;38}39else {40const uintptr_t segindex = ((uintptr_t)segment) / MI_SEGMENT_SIZE;41*bitidx = segindex % MI_INTPTR_BITS;42const size_t mapindex = segindex / MI_INTPTR_BITS;43mi_assert_internal(mapindex < MI_SEGMENT_MAP_WSIZE);44return mapindex;45}46}4748void _mi_segment_map_allocated_at(const mi_segment_t* segment) {49size_t bitidx;50size_t index = mi_segment_map_index_of(segment, &bitidx);51mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE);52if (index==MI_SEGMENT_MAP_WSIZE) return;53uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]);54uintptr_t newmask;55do {56newmask = (mask | ((uintptr_t)1 << bitidx));57} while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask));58}5960void _mi_segment_map_freed_at(const mi_segment_t* segment) {61size_t bitidx;62size_t index = mi_segment_map_index_of(segment, &bitidx);63mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE);64if (index == MI_SEGMENT_MAP_WSIZE) return;65uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]);66uintptr_t newmask;67do {68newmask = (mask & ~((uintptr_t)1 << bitidx));69} while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask));70}7172// Determine the segment belonging to a pointer or NULL if it is not in a valid segment.73static mi_segment_t* _mi_segment_of(const void* p) {74if (p == NULL) return NULL;75mi_segment_t* segment = _mi_ptr_segment(p); // segment can be NULL76size_t bitidx;77size_t index = mi_segment_map_index_of(segment, &bitidx);78// fast path: for any pointer to valid small/medium/large object or first MI_SEGMENT_SIZE in huge79const uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]);80if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) {81return segment; // yes, allocated by us82}83if (index==MI_SEGMENT_MAP_WSIZE) return NULL;8485// TODO: maintain max/min allocated range for efficiency for more efficient rejection of invalid pointers?8687// search downwards for the first segment in case it is an interior pointer88// could be slow but searches in MI_INTPTR_SIZE * MI_SEGMENT_SIZE (512MiB) steps trough89// valid huge objects90// note: we could maintain a lowest index to speed up the path for invalid pointers?91size_t lobitidx;92size_t loindex;93uintptr_t lobits = mask & (((uintptr_t)1 << bitidx) - 1);94if (lobits != 0) {95loindex = index;96lobitidx = mi_bsr(lobits); // lobits != 097}98else if (index == 0) {99return NULL;100}101else {102mi_assert_internal(index > 0);103uintptr_t lomask = mask;104loindex = index;105do {106loindex--;107lomask = mi_atomic_load_relaxed(&mi_segment_map[loindex]);108} while (lomask != 0 && loindex > 0);109if (lomask == 0) return NULL;110lobitidx = mi_bsr(lomask); // lomask != 0111}112mi_assert_internal(loindex < MI_SEGMENT_MAP_WSIZE);113// take difference as the addresses could be larger than the MAX_ADDRESS space.114size_t diff = (((index - loindex) * (8*MI_INTPTR_SIZE)) + bitidx - lobitidx) * MI_SEGMENT_SIZE;115segment = (mi_segment_t*)((uint8_t*)segment - diff);116117if (segment == NULL) return NULL;118mi_assert_internal((void*)segment < p);119bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie);120mi_assert_internal(cookie_ok);121if mi_unlikely(!cookie_ok) return NULL;122if (((uint8_t*)segment + mi_segment_size(segment)) <= (uint8_t*)p) return NULL; // outside the range123mi_assert_internal(p >= (void*)segment && (uint8_t*)p < (uint8_t*)segment + mi_segment_size(segment));124return segment;125}126127// Is this a valid pointer in our heap?128static bool mi_is_valid_pointer(const void* p) {129return ((_mi_segment_of(p) != NULL) || (_mi_arena_contains(p)));130}131132mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept {133return mi_is_valid_pointer(p);134}135136/*137// Return the full segment range belonging to a pointer138static void* mi_segment_range_of(const void* p, size_t* size) {139mi_segment_t* segment = _mi_segment_of(p);140if (segment == NULL) {141if (size != NULL) *size = 0;142return NULL;143}144else {145if (size != NULL) *size = segment->segment_size;146return segment;147}148mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld));149mi_assert_internal(page == NULL || (mi_segment_page_size(_mi_page_segment(page)) - (MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= block_size);150mi_reset_delayed(tld);151mi_assert_internal(page == NULL || mi_page_not_in_queue(page, tld));152return page;153}154*/155156157