Loading...
src/xzone_malloc/xzone_inline_internal.h /dev/null libmalloc-792.60.6
--- /dev/null
+++ libmalloc/libmalloc-792.60.6/src/xzone_malloc/xzone_inline_internal.h
@@ -0,0 +1,1350 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2022, Microsoft Research, Daan Leijen
+Copyright © 2025 Apple Inc.
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" in the same directory as this file.
+-----------------------------------------------------------------------------*/
+
+#if CONFIG_XZONE_MALLOC
+
+#ifndef __XZONE_INLINE_INTERNAL_H__
+#define __XZONE_INLINE_INTERNAL_H__
+
+#if !__has_feature(bounds_safety)
+
+#define xzm_abort(msg)  ({ \
+	_os_set_crash_log_cause_and_message(0, "BUG IN LIBMALLOC: " msg); \
+	__builtin_trap(); \
+})
+#define xzm_abort_with_reason(msg, reason)  ({ \
+	_os_set_crash_log_cause_and_message((reason), "BUG IN LIBMALLOC: " msg); \
+	__builtin_trap(); \
+})
+#define xzm_client_abort(msg)  ({ \
+	_os_set_crash_log_cause_and_message(0, "BUG IN CLIENT OF LIBMALLOC: " msg); \
+	__builtin_trap(); \
+})
+#define xzm_client_abort_with_reason(msg, reason)  ({ \
+	_os_set_crash_log_cause_and_message((reason), "BUG IN CLIENT OF LIBMALLOC: " msg); \
+	__builtin_trap(); \
+})
+
+#define _xzm_assert_stringify(x) #x
+#define xzm_assert_stringify(x) _xzm_assert_stringify(x)
+
+// mimalloc: mi_assert
+#define xzm_assert(pred) \
+	if (os_unlikely(!(pred))) { \
+		xzm_abort("malloc assertion \"" #pred "\" failed " \
+				"(" __FILE__ ":" xzm_assert_stringify(__LINE__) ")"); \
+	}
+
+// mimalloc: mi_assert_internal
+#ifdef DEBUG
+#define xzm_debug_assert xzm_assert
+#define xzm_debug_abort xzm_abort
+#define xzm_debug_abort_with_reason xzm_abort_with_reason
+#ifndef __assert_only
+#define __assert_only
+#endif
+#else // DEBUG
+#define xzm_debug_assert(...)
+#define xzm_debug_abort(...)
+#define xzm_debug_abort_with_reason(...)
+#ifndef __assert_only
+#define __assert_only __unused
+#endif
+#endif // DEBUG
+
+MALLOC_INLINE
+static void
+_xzm_corruption_detected(void *corrupt_block)
+{
+	// TODO: load the corrupt value into a register so it also appears in crash
+	// reports
+	xzm_client_abort_with_reason("memory corruption of free block",
+			corrupt_block);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_main_malloc_zone_t
+_xzm_malloc_zone_main(xzm_malloc_zone_t zone)
+{
+	return zone->xzz_main_ref ?: (xzm_main_malloc_zone_t)zone;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_malloc_zone_is_main(xzm_malloc_zone_t zone)
+{
+	return !zone->xzz_main_ref;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_malloc_zone_is_xzm(malloc_zone_t *zone)
+{
+	return zone->version >= 14 &&
+			zone->introspect->zone_type == MALLOC_ZONE_TYPE_XZONE;
+}
+
+#pragma mark magic math
+
+// see https://lemire.me/blog/2019/02/20/more-fun-with-fast-remainders-when-the-divisor-is-a-constant/
+//
+// Implementation copied from zalloc
+#define XZM_MAGIC_QUO(s)      (((1ull << 32) - 1) / (uint64_t)(s) + 1)
+#define XZM_MAGIC_ALIGNED(s)  (~0u / (uint32_t)(s) + 1)
+
+// Returns (offs / size) if offs is small enough and magic = XZM_MAGIC_QUO(size)
+static inline uint32_t
+XZM_FAST_QUO(uint64_t offs, uint64_t __unused size, uint64_t magic)
+{
+	uint32_t quo = (offs * magic) >> 32;
+	xzm_debug_assert(offs / size == quo);
+	return quo;
+}
+
+// Returns (offs % size) if offs is small enough and magic ==
+// XZM_MAGIC_QUO(size)
+static inline uint32_t
+XZM_FAST_MOD(uint64_t offs, uint64_t magic, uint64_t size)
+{
+	uint32_t lowbits = (uint32_t)(offs * magic);
+
+	uint32_t mod = (lowbits * size) >> 32;
+	xzm_debug_assert(offs % size == mod);
+	return mod;
+}
+
+// Returns whether (offs % size) == 0 if offs is small enough and magic ==
+// XZM_MAGIC_ALIGNED(size)
+static inline bool
+XZM_FAST_ALIGNED(uint64_t offs, uint64_t __unused size, uint32_t magic)
+{
+	bool aligned = (uint32_t)(offs * magic) < magic;
+	xzm_debug_assert(aligned == ((offs % size) == 0));
+	return aligned;
+}
+
+#pragma mark metadata helpers
+
+// mimalloc: mi_segment_map_index_of
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static size_t
+_xzm_segment_table_index_of(const void *segment_body, size_t *extended_idx)
+{
+	uintptr_t segment_bits = (uintptr_t)segment_body;
+	if (segment_bits >= XZM_LIMIT_ADDRESS) {
+		*extended_idx = 0;
+		return XZM_SEGMENT_TABLE_ENTRIES;
+	}
+
+	uintptr_t segindex = segment_bits / XZM_SEGMENT_SIZE;
+#if CONFIG_EXTERNAL_METADATA_LARGE
+	// The segment map index we return in the large address space is the index
+	// into the bottom level table, the extended_idx is the index into top table
+	// (setting extended_idx = 0 implies that the body is in the low 64GB of VA)
+	*extended_idx = segindex / XZM_SEGMENT_TABLE_ENTRIES;
+	xzm_debug_assert(*extended_idx < XZM_EXTENDED_SEGMENT_TABLE_ENTRIES);
+	return segindex % XZM_SEGMENT_TABLE_ENTRIES;
+#else
+	*extended_idx = 0;
+	xzm_debug_assert(segindex < XZM_SEGMENT_TABLE_ENTRIES);
+	return segindex;
+#endif // CONFIG_EXTERNAL_METADATA_LARGE
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_segment_t
+_xzm_segment_table_entry_to_segment(xzm_segment_table_entry_s entry)
+{
+	return (xzm_segment_t)
+			((uintptr_t)entry.xste_val << XZM_METAPOOL_SEGMENT_BLOCK_SHIFT);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_segment_table_entry_s
+_xzm_segment_to_segment_table_entry(xzm_segment_t segment, bool normal)
+{
+	xzm_debug_assert((uintptr_t)segment % XZM_METAPOOL_SEGMENT_ALIGN == 0);
+	// TODO: On MacOS (47 bit address space), the upper portion of the address
+	// space won't fit in this encoding
+	xzm_assert(((uintptr_t)segment >> XZM_METAPOOL_SEGMENT_BLOCK_SHIFT) <
+			XZM_SEGMENT_TABLE_LIMIT_ENTRY);
+	return (xzm_segment_table_entry_s) {
+		.xste_val = (uint32_t)
+				((uintptr_t)segment >> XZM_METAPOOL_SEGMENT_BLOCK_SHIFT),
+		.xste_normal = normal,
+	};
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_segment_table_entry_s *
+_xzm_ptr_to_table_entry(const void *segment_body,
+		xzm_main_malloc_zone_t main)
+{
+	size_t ext_idx = 0;
+	size_t index = _xzm_segment_table_index_of(segment_body, &ext_idx);
+#if CONFIG_EXTERNAL_METADATA_LARGE
+	if (ext_idx == 0) {
+		if (os_unlikely(index >= XZM_SEGMENT_TABLE_ENTRIES)) {
+			// Pointer out of bounds, greater than XZM_LIMIT_ADDRESS
+			return NULL;
+		}
+		// This pointer is in the first 64GB of VA, so it comes directly from
+		// the segment table in the main zone
+		xzm_debug_assert((uintptr_t)segment_body < XZM_SEGMENT_TABLE_COVERAGE);
+		return &main->xzmz_segment_table[index];
+	} else if (ext_idx >= XZM_EXTENDED_SEGMENT_TABLE_ENTRIES) {
+		return NULL;
+	} else {
+		xzm_segment_table_entry_s *leaf_table;
+		xzm_extended_segment_table_entry_s *map;
+		map = main->xzmz_extended_segment_table;
+		xzm_debug_assert(map != 0);
+		leaf_table = (void *)((uintptr_t)(map[ext_idx].xeste_val) *
+				XZM_SEGMENT_TABLE_ALIGN);
+		if (leaf_table == NULL) {
+			// there are no segments in the given 64GB span
+			return NULL;
+		}
+		return &leaf_table[index];
+	}
+#else
+	if (index >= XZM_SEGMENT_TABLE_ENTRIES) {
+		return NULL;
+	}
+	return &main->xzmz_segment_table[index];
+#endif // CONFIG_EXTERNAL_METADATA_LARGE
+
+}
+
+// mimalloc: _mi_segment_of
+// Note: This will allow inner pointers, or any pointer inside the (4MB) segment
+// granule of an allocated segment
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_segment_t
+xzm_segment_table_query(xzm_main_malloc_zone_t main, const void *ptr)
+{
+	xzm_segment_table_entry_s *leaf_entry;
+	leaf_entry = _xzm_ptr_to_table_entry(ptr, main);
+	if (leaf_entry == NULL) {
+		return NULL;
+	}
+	return _xzm_segment_table_entry_to_segment(*leaf_entry);
+}
+
+// mimalloc: _mi_page_segment
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_segment_t
+_xzm_segment_for_slice(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	xzm_segment_t segment = (xzm_segment_t)
+			((uintptr_t)slice & ~(XZM_METAPOOL_SEGMENT_BLOCK_SIZE - 1));
+	xzm_debug_assert(!segment || (slice >= segment->xzs_slices &&
+			slice < (segment->xzs_slices + segment->xzs_slice_entry_count)));
+	return segment;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_segment_group_t
+_xzm_segment_group_for_slice(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	xzm_segment_t segment = _xzm_segment_for_slice(zone, slice);
+	return segment->xzs_segment_group;
+}
+
+// mimalloc: mi_slice_index
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_slice_index(xzm_segment_t segment, xzm_slice_t slice)
+{
+	xzm_debug_assert((uintptr_t)slice >= (uintptr_t)segment->xzs_slices);
+	ptrdiff_t index = slice - segment->xzs_slices;
+	xzm_debug_assert(index < (ptrdiff_t)segment->xzs_slice_entry_count);
+	return (xzm_slice_count_t)index;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static const char *
+_xzm_range_group_id_to_string(xzm_range_group_id_t id)
+{
+	switch(id) {
+	case XZM_RANGE_GROUP_DATA:
+		return "data";
+	case XZM_RANGE_GROUP_PTR:
+		return "pointer";
+	default:
+		return "unknown";
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static const char *
+_xzm_segment_group_id_to_string(xzm_segment_group_id_t id)
+{
+	switch(id) {
+	case XZM_SEGMENT_GROUP_DATA:
+		return "data";
+	case XZM_SEGMENT_GROUP_DATA_LARGE:
+		return "data_large";
+	case XZM_SEGMENT_GROUP_POINTER_XZONES:
+		return "pointer_xzones";
+	case XZM_SEGMENT_GROUP_POINTER_LARGE:
+		return "pointer_large";
+	default:
+		return "unknown";
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_segment_group_id_is_data(xzm_segment_group_id_t id)
+{
+	switch(id) {
+	case XZM_SEGMENT_GROUP_DATA:
+	case XZM_SEGMENT_GROUP_DATA_LARGE:
+		return true;
+	case XZM_SEGMENT_GROUP_POINTER_XZONES:
+	case XZM_SEGMENT_GROUP_POINTER_LARGE:
+		return false;
+	default:
+		xzm_abort_with_reason("unknown segment group id", id);
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_slice_index_start(xzm_segment_t segment, xzm_slice_count_t idx)
+{
+	return (uint8_t *)((uintptr_t)segment->xzs_segment_body +
+			(idx * XZM_SEGMENT_SLICE_SIZE));
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_slice_start(xzm_segment_t segment, xzm_slice_t slice)
+{
+	return _xzm_segment_slice_index_start(segment,
+			_xzm_slice_index(segment, slice));
+	// TODO: mimalloc offset optimization for small block sizes?
+}
+
+// FIXME: `zone` parameter isn't used in _xzm_segment_for_slice(),
+// _xzm_slice_start(), _xzm_chunk_start(), _xzm_chunk_start_ptr() and can be
+// removed.
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_slice_start(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	return _xzm_segment_slice_start(_xzm_segment_for_slice(zone, slice), slice);
+}
+
+// mimalloc: _mi_page_start
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uintptr_t
+_xzm_chunk_start(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		size_t *chunk_size_out)
+{
+	if (chunk_size_out) {
+		switch (chunk->xzc_bits.xzcb_kind) {
+		case XZM_SLICE_KIND_TINY_CHUNK:
+			*chunk_size_out = XZM_TINY_CHUNK_SIZE;
+			break;
+		case XZM_SLICE_KIND_SMALL_CHUNK:
+			*chunk_size_out = XZM_SMALL_CHUNK_SIZE;
+			break;
+		case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+			*chunk_size_out = XZM_SMALL_FREELIST_CHUNK_SIZE;
+			break;
+		case XZM_SLICE_KIND_LARGE_CHUNK:
+		case XZM_SLICE_KIND_HUGE_CHUNK:
+			*chunk_size_out = ((size_t)chunk->xzcs_slice_count) <<
+					XZM_SEGMENT_SLICE_SHIFT;
+			break;
+		default:
+			xzm_abort_with_reason("asking for start of chunk with invalid kind",
+					(unsigned)chunk->xzc_bits.xzcb_kind);
+		}
+	}
+
+	return (uintptr_t)_xzm_slice_start(zone, (xzm_slice_t)chunk);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_chunk_start_ptr(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		size_t *chunk_size_out)
+{
+	xzm_debug_assert(chunk_size_out);
+	uintptr_t ptr = _xzm_chunk_start(zone, chunk, chunk_size_out);
+	return (uint8_t *)ptr;
+}
+
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_segment_slice_count(xzm_segment_t segment)
+{
+	return segment->xzs_slice_count;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static size_t
+_xzm_segment_size(xzm_segment_t segment)
+{
+	xzm_slice_count_t body_slice_count = _xzm_segment_slice_count(segment);
+	return (size_t)body_slice_count << XZM_SEGMENT_SLICE_SHIFT;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_start(xzm_segment_t segment)
+{
+	return _xzm_segment_slice_index_start(segment, 0);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static size_t
+_xzm_segment_slice_offset_of(xzm_segment_t segment, uintptr_t ptr)
+{
+	const ptrdiff_t diff = ptr - (uintptr_t)_xzm_segment_start(segment);
+#ifdef DEBUG
+	// Huge segments aren't always a multiple of the segment size, so it's
+	// possible for malloc_size() to be passed a pointer that is within a
+	// segment granule, but isn't within the segment that owns that granule. We
+	// need to not crash in the debug dylib when that happens
+	size_t rounded_size = roundup(_xzm_segment_size(segment), XZM_SEGMENT_SIZE);
+	xzm_debug_assert(diff >= 0 && diff < (ptrdiff_t)rounded_size);
+#endif // DEBUG
+	return (size_t)diff;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_segment_slice_offset_index(xzm_segment_t segment, size_t offset)
+{
+	return (xzm_slice_count_t)(offset >> XZM_SEGMENT_SLICE_SHIFT);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_segment_slice_index_of(xzm_segment_t segment, uintptr_t ptr)
+{
+	size_t offset = _xzm_segment_slice_offset_of(segment, ptr);
+	return _xzm_segment_slice_offset_index(segment, offset);
+}
+
+// mimalloc: _mi_segment_page_of
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_t
+_xzm_segment_slice_of(xzm_segment_t segment, uintptr_t ptr)
+{
+	xzm_slice_count_t idx = _xzm_segment_slice_index_of(segment, ptr);
+	if (os_likely(idx < segment->xzs_slice_entry_count)) {
+		return &segment->xzs_slices[idx];
+	} else {
+		return NULL;
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_slice_index_end(xzm_segment_t segment, xzm_slice_count_t idx)
+{
+	return _xzm_segment_slice_index_start(segment, idx + 1);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_slice_end_of(xzm_segment_t segment, uintptr_t ptr)
+{
+	xzm_slice_count_t idx = _xzm_segment_slice_index_of(segment, ptr);
+	return _xzm_segment_slice_index_end(segment, idx);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_end(xzm_segment_t segment)
+{
+	return _xzm_segment_slice_index_end(segment, segment->xzs_slice_count - 1);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_segment_slice_start_of(xzm_segment_t segment, uintptr_t ptr)
+{
+	xzm_slice_count_t idx = _xzm_segment_slice_index_of(segment, ptr);
+	return _xzm_segment_slice_index_start(segment, idx);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_t
+_xzm_segment_slices_begin(xzm_segment_t segment)
+{
+	return &segment->xzs_slices[0];
+}
+
+// mimalloc: mi_segment_slices_end
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_t
+_xzm_segment_slices_end(xzm_segment_t segment)
+{
+	// Return a one-past-the-end pointer without immediately trapping
+	return &segment->xzs_slices[segment->xzs_slice_entry_count];
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static const char *
+_xzm_segment_kind_to_string(xzm_segment_kind_t kind)
+{
+	switch(kind) {
+	case XZM_SEGMENT_KIND_NORMAL:
+		return "normal_segment";
+	case XZM_SEGMENT_KIND_HUGE:
+		return "huge_segment";
+	default:
+		return "unknown";
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static const char *
+_xzm_metapool_id_to_string(xzm_metapool_id_t id)
+{
+	switch(id) {
+	case XZM_METAPOOL_SEGMENT:
+		return "segment metadata slab";
+	case XZM_METAPOOL_SEGMENT_TABLE:
+		return "segment table slab";
+	case XZM_METAPOOL_MZONE_IDX:
+		return "mzone index slab";
+	case XZM_METAPOOL_THREAD_CACHE:
+		return "thread cache slab";
+	case XZM_METAPOOL_METADATA:
+		return "metapool metadata slab";
+	default:
+		return "unknown slab";
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_span_contains_slice(xzm_slice_t span, xzm_slice_t slice)
+{
+	switch (span->xzc_bits.xzcb_kind) {
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SINGLE_FREE:
+		return (span == slice);
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+	case XZM_SLICE_KIND_LARGE_CHUNK:
+	case XZM_SLICE_KIND_HUGE_CHUNK:
+	case XZM_SLICE_KIND_MULTI_FREE:
+	case XZM_SLICE_KIND_GUARD:
+		xzm_debug_assert(slice >= span);
+		return (slice < span + span->xzcs_slice_count);
+	default:
+		return false;
+	}
+}
+
+// mimalloc: mi_slice_first
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_t
+_xzm_span_slice_first(xzm_slice_t slice)
+{
+	// "likely" in the sense that the most common case for lookups will be tiny
+	// chunks
+	if (os_likely(slice->xzc_bits.xzcb_kind != XZM_SLICE_KIND_MULTI_BODY)) {
+		return slice;
+	}
+
+	xzm_slice_t out_slice = (xzm_slice_t)
+			((uintptr_t)slice - slice->xzsl_slice_offset_bytes);
+
+	xzm_debug_assert(out_slice >= ((xzm_segment_t)((uintptr_t)slice &
+			~(XZM_METAPOOL_SEGMENT_BLOCK_SIZE - 1)))->xzs_slices);
+	if (os_likely(_xzm_span_contains_slice(out_slice, slice))) {
+		return out_slice;
+	}
+	// not contained in the span we refer to - leave it up to the caller to
+	// handle this
+	return slice;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_slice_kind_is_chunk(xzm_slice_kind_t kind)
+{
+	switch (kind) {
+	case XZM_SLICE_KIND_INVALID:
+	case XZM_SLICE_KIND_SINGLE_FREE:
+	case XZM_SLICE_KIND_MULTI_FREE:
+	case XZM_SLICE_KIND_MULTI_BODY:
+		return false;
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+	case XZM_SLICE_KIND_LARGE_CHUNK:
+	case XZM_SLICE_KIND_HUGE_CHUNK:
+		return true;
+	default:
+		xzm_abort_with_reason("bad chunk kind", (unsigned)kind);
+	}
+}
+
+// Like _xzm_slice_kind_is_chunk, but doesn't abort on totally bogus kinds
+// (useful during enumeration when we have no guarantees about what we're
+// looking at)
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_slice_kind_is_chunk_safe(xzm_slice_kind_t kind)
+{
+	switch (kind) {
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+	case XZM_SLICE_KIND_LARGE_CHUNK:
+	case XZM_SLICE_KIND_HUGE_CHUNK:
+		return true;
+	default:
+		return false;
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_slice_kind_uses_xzones(xzm_slice_kind_t kind)
+{
+	switch (kind) {
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		return true;
+	default:
+		return false;
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_chunk_should_defer_reclamation(xzm_main_malloc_zone_t main,
+		xzm_chunk_t chunk)
+{
+	xzm_debug_assert(_xzm_slice_kind_is_chunk(chunk->xzc_bits.xzcb_kind));
+	switch (chunk->xzc_bits.xzcb_kind) {
+	case XZM_SLICE_KIND_TINY_CHUNK:
+		return main->xzmz_defer_tiny;
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		return main->xzmz_defer_small;
+	case XZM_SLICE_KIND_LARGE_CHUNK:
+	case XZM_SLICE_KIND_HUGE_CHUNK:
+		return main->xzmz_defer_large;
+	default:
+		xzm_abort("Attempt to check for deferred reclamation on "
+				"non-chunk slice");
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static const char *
+_xzm_slice_kind_to_string(xzm_slice_kind_t kind)
+{
+	switch (kind) {
+	case XZM_SLICE_KIND_INVALID:
+		return "invalid";
+	case XZM_SLICE_KIND_SINGLE_FREE:
+		return "single_free";
+	case XZM_SLICE_KIND_MULTI_FREE:
+		return "multi_free";
+	case XZM_SLICE_KIND_MULTI_BODY:
+		return "multi_body";
+	case XZM_SLICE_KIND_TINY_CHUNK:
+		return "tiny_chunk";
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+		return "small_chunk";
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		return "small_freelist_chunk";
+	case XZM_SLICE_KIND_LARGE_CHUNK:
+		return "large_chunk";
+	case XZM_SLICE_KIND_HUGE_CHUNK:
+		return "huge_chunk";
+	case XZM_SLICE_KIND_GUARD:
+		return "guard_page";
+	default:
+		return "unknown";
+	}
+}
+
+// mimalloc: _mi_segment_page_of
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_chunk_t
+_xzm_segment_chunk_of(xzm_segment_t segment, uintptr_t ptr)
+{
+	xzm_slice_t slice = _xzm_segment_slice_of(segment, ptr);
+	if (!slice) {
+		return NULL;
+	}
+
+	xzm_slice_t first = _xzm_span_slice_first(slice);
+	return _xzm_slice_kind_is_chunk(first->xzc_bits.xzcb_kind) ? first : NULL;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static size_t
+_xzm_segment_offset(xzm_segment_t segment, xzm_slice_count_t chunk_idx,
+		xzm_block_index_t block_idx, uint64_t block_size)
+{
+	return chunk_idx * XZM_SEGMENT_SLICE_SIZE + block_idx * block_size;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_block_index_t
+_xzm_segment_offset_chunk_block_index_of(xzm_segment_t segment,
+		xzm_slice_count_t chunk_idx, uint64_t block_size, size_t offset)
+{
+	xzm_debug_assert(offset >= chunk_idx * XZM_SEGMENT_SLICE_SIZE);
+	return (xzm_block_index_t)
+			(offset - chunk_idx * XZM_SEGMENT_SLICE_SIZE) / block_size;
+}
+
+// mimalloc: mi_page_block_size
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint64_t
+_xzm_chunk_block_size(xzm_malloc_zone_t zone, xzm_chunk_t chunk)
+{
+	switch (chunk->xzc_bits.xzcb_kind) {
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		// TODO: depending on the size class scheme, it may be better to
+		// directly compute the block size from the xzone index using the
+		// inverse of the bin function
+		return zone->xzz_xzones[chunk->xzc_xzone_idx].xz_block_size;
+	case XZM_SLICE_KIND_LARGE_CHUNK:
+	case XZM_SLICE_KIND_HUGE_CHUNK:
+		return ((uint64_t)chunk->xzcs_slice_count) << XZM_SEGMENT_SLICE_SHIFT;
+	default:
+		xzm_abort_with_reason("asking for size of chunk with invalid kind",
+				(unsigned)chunk->xzc_bits.xzcb_kind);
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_chunk_slice_count(xzm_chunk_t chunk)
+{
+	return (chunk->xzc_bits.xzcb_kind == XZM_SLICE_KIND_TINY_CHUNK) ? 1 :
+			chunk->xzcs_slice_count;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_t
+_xzm_chunk_slices_of(xzm_chunk_t chunk, size_t num_slices)
+{
+	return (xzm_slice_t)chunk;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_chunk_is_empty(xzm_malloc_zone_t zone, xzm_xzone_t xz, xzm_chunk_t chunk)
+{
+	switch (chunk->xzc_bits.xzcb_kind) {
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+		return chunk->xzc_used == 0;
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		return chunk->xzc_atomic_meta.xca_free_count == xz->xz_chunk_capacity ||
+				chunk->xzc_atomic_meta.xca_alloc_head == XZM_FREE_MADVISING ||
+				chunk->xzc_atomic_meta.xca_alloc_head == XZM_FREE_MADVISED;
+	default:
+		xzm_abort_with_reason("bad chunk kind",
+				(unsigned int)chunk->xzc_bits.xzcb_kind);
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_chunk_is_full(xzm_malloc_zone_t zone, xzm_xzone_t xz, xzm_chunk_t chunk)
+{
+	switch (chunk->xzc_bits.xzcb_kind) {
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+		return (chunk->xzc_used == xz->xz_chunk_capacity);
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		return chunk->xzc_atomic_meta.xca_free_count == 0 &&
+				chunk->xzc_atomic_meta.xca_alloc_head != XZM_FREE_MADVISING &&
+				chunk->xzc_atomic_meta.xca_alloc_head != XZM_FREE_MADVISED;
+	default:
+		xzm_abort_with_reason("bad chunk kind",
+				(unsigned int)chunk->xzc_bits.xzcb_kind);
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint32_t
+_xzm_xzone_free_mask(xzm_xzone_t xz, size_t chunk_capacity)
+{
+	xzm_debug_assert(!xz || xz->xz_chunk_capacity == chunk_capacity);
+	return (uint32_t)((1ull << chunk_capacity) - 1);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static void
+_xzm_chunk_reset_free(xzm_xzone_t xz, xzm_chunk_t chunk, bool reusable)
+{
+	if (!reusable) {
+		chunk->xzc_xzone_idx = XZM_XZONE_INDEX_INVALID;
+	}
+
+	switch (chunk->xzc_bits.xzcb_kind) {
+	case XZM_SLICE_KIND_TINY_CHUNK:
+	case XZM_SLICE_KIND_SMALL_FREELIST_CHUNK:
+		// Tiny chunks should only be freed when a zone is destroyed, at which
+		// point we don't need to support concurrent access to those chunks. As
+		// such, we can access the atomic state non-atomically in this path
+		xzm_debug_assert(chunk->xzc_atomic_meta.xca_alloc_head ==
+				XZM_FREE_MADVISED);
+		xzm_debug_assert(chunk->xzc_atomic_meta.xca_free_count == 0);
+
+		if (!reusable) {
+			// Reset everything to 0 to allow reuse of this slice for any
+			// purpose
+			chunk->xzc_atomic_meta.xca_value = 0;
+			chunk->xzc_freelist_block_size = 0;
+			chunk->xzc_freelist_chunk_capacity = 0;
+#if CONFIG_MTE
+			chunk->xzc_tagged = false;
+#endif
+		}
+		break;
+	case XZM_SLICE_KIND_SMALL_CHUNK:
+		chunk->xzc_used = 0;
+		chunk->xzc_alloc_idx = XZM_SLOT_INDEX_EMPTY;
+		if (reusable) {
+			chunk->xzc_free |= _xzm_xzone_free_mask(xz, xz->xz_chunk_capacity);
+		} else {
+			chunk->xzc_free = 0;
+		}
+		break;
+	default:
+		xzm_abort_with_reason("bad chunk kind",
+				(unsigned int)chunk->xzc_bits.xzcb_kind);
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_free_span_slice_count(xzm_free_span_t span)
+{
+	return (span->xzc_bits.xzcb_kind == XZM_SLICE_KIND_SINGLE_FREE) ? 1 :
+			span->xzcs_slice_count;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_slice_count_t
+_xzm_free_span_size(xzm_free_span_t span)
+{
+	return _xzm_free_span_slice_count(span) << XZM_SEGMENT_SLICE_SHIFT;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_block_offset_t
+_xzm_chunk_offset_of_ptr(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		uintptr_t ptr)
+{
+	uintptr_t start = _xzm_chunk_start(zone, chunk, NULL);
+#if CONFIG_MTE
+	// Remove tag bits for pointer arithmetic
+	ptr = (uintptr_t)memtag_strip_address((uint8_t *)ptr);
+#endif
+	xzm_block_offset_t offset = (xzm_block_offset_t)(ptr - start);
+	return offset;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_block_offset_t
+_xzm_chunk_block_offset(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		xzm_block_t block)
+{
+	return _xzm_chunk_offset_of_ptr(zone, chunk, (uintptr_t)block);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_chunk_block_start_of(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		uintptr_t ptr)
+{
+	xzm_debug_assert(chunk);
+	xzm_debug_assert(ptr);
+
+	xzm_block_offset_t offset = _xzm_chunk_offset_of_ptr(zone, chunk, ptr);
+	size_t adjust = offset % _xzm_chunk_block_size(zone, chunk);
+	return (uint8_t *)(ptr - adjust);
+}
+
+// mimalloc: _mi_page_ptr_unalign
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_block_t
+_xzm_chunk_block_of(xzm_malloc_zone_t zone, xzm_chunk_t chunk, uintptr_t ptr)
+{
+	return (xzm_block_t)_xzm_chunk_block_start_of(zone, chunk, ptr);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_block_index_t
+_xzm_chunk_block_index(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		xzm_block_t block)
+{
+	return (xzm_block_index_t)(_xzm_chunk_block_offset(zone, chunk, block) /
+			_xzm_chunk_block_size(zone, chunk));
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_block_index_t
+_xzm_chunk_block_index_of_ptr(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		uintptr_t ptr)
+{
+	return (xzm_block_index_t)(_xzm_chunk_offset_of_ptr(zone, chunk, ptr) /
+			_xzm_chunk_block_size(zone, chunk));
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint8_t *
+_xzm_chunk_block_index_start(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		xzm_block_index_t idx)
+{
+	xzm_debug_assert(idx <
+			zone->xzz_xzones[chunk->xzc_xzone_idx].xz_chunk_capacity);
+	return (uint8_t *)(_xzm_chunk_start(zone, chunk, NULL) +
+			(idx * _xzm_chunk_block_size(zone, chunk)));
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_small_chunk_block_index_is_free(xzm_chunk_t chunk,
+		xzm_block_index_t block_index)
+{
+	// Only applicable to chunks which use a bitmap freelist implementation
+	xzm_debug_assert(chunk->xzc_bits.xzcb_kind == XZM_SLICE_KIND_SMALL_CHUNK);
+	return (bool)(chunk->xzc_free & (1u << block_index));
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_chunk_block_index_range_is_free(xzm_malloc_zone_t zone, xzm_chunk_t chunk,
+		xzm_block_index_t start, xzm_block_index_t end)
+{
+	// Only applicable to chunks which use a bitmap freelist implementation
+	xzm_debug_assert(chunk->xzc_bits.xzcb_kind == XZM_SLICE_KIND_SMALL_CHUNK);
+	xzm_debug_assert(end >= start);
+
+	// Check the inclusive span from start to end
+	uint32_t span = (end - start) + 1;
+	xzm_debug_assert(span <= 32);
+
+	uint32_t mask = (uint32_t)(((1ull << span) - 1) << start);
+	return (chunk->xzc_free & mask) == mask;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static void
+_xzm_chunk_block_free_slices_on_allocate(const xzm_chunk_t chunk,
+		xzm_slice_count_t chunk_idx, uint32_t chunk_capacity,
+		xzm_block_index_t block_idx, uint64_t block_size,
+		xzm_slice_count_t *slice_idx, xzm_slice_count_t *num_slices)
+{
+	xzm_debug_assert(chunk &&
+			chunk->xzc_bits.xzcb_kind == XZM_SLICE_KIND_SMALL_CHUNK);
+
+	// Offset of this block relative to the body of the segment
+	const size_t block = _xzm_segment_offset(NULL, chunk_idx, block_idx,
+			block_size);
+	const size_t block_end = block + block_size - 1;
+
+	// Find the beginning of the first slice touched by the block
+	const xzm_slice_count_t first_slice =
+			_xzm_segment_slice_offset_index(NULL, block);
+	// Find the beginning of the slice after the last touched by the block
+	const xzm_slice_count_t limit_slice =
+			_xzm_segment_slice_offset_index(NULL, block_end) + 1;
+
+	// Find the blocks corresponding to these slices
+	const xzm_block_index_t first_block =
+			_xzm_segment_offset_chunk_block_index_of(NULL, chunk_idx, block_size,
+			first_slice * XZM_SEGMENT_SLICE_SIZE);
+	const xzm_block_index_t end_block =
+			_xzm_segment_offset_chunk_block_index_of(NULL, chunk_idx, block_size,
+			limit_slice * XZM_SEGMENT_SLICE_SIZE - 1);
+
+	// Compute the offset of the first slice to populate, by checking if
+	// any of the affected blocks are already in use (not all free).
+	// If so, then we start with the end of the slice corresponding to the
+	// beginning of our current block, instead of the beginning of that
+	// slice
+	const xzm_slice_count_t left = !_xzm_chunk_block_index_range_is_free(NULL,
+			chunk, first_block, block_idx) ?
+			_xzm_segment_slice_offset_index(NULL, block) + 1 : first_slice;
+
+	// Compute the offset of the last slice to populate, by checking if
+	// any of the affected blocks are already in use, as above.
+	// If so, then we end with the beginning of the slice corresponding to
+	// the end of our current block, instead of the end of that slice.
+	// Exclude the end block if it is partial, because it is never free
+	const xzm_block_index_t last_block =
+			(end_block != chunk_capacity ? end_block : end_block - 1);
+	xzm_debug_assert(block_idx <= last_block && last_block < chunk_capacity);
+	const xzm_slice_count_t right = !_xzm_chunk_block_index_range_is_free(NULL,
+			chunk, block_idx, last_block) ?
+			_xzm_segment_slice_offset_index(NULL, block_end) : limit_slice;
+
+	*slice_idx = left;
+	*num_slices = (left <= right ? right - left : 0);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static void
+_xzm_chunk_block_free_slices_on_deallocate(const xzm_chunk_t chunk,
+		xzm_slice_count_t chunk_idx, uint32_t chunk_capacity,
+		xzm_block_index_t block_idx, uint64_t block_size,
+		xzm_slice_count_t *slice_idx, xzm_slice_count_t *num_slices)
+{
+	xzm_debug_assert(chunk &&
+			chunk->xzc_bits.xzcb_kind == XZM_SLICE_KIND_SMALL_CHUNK);
+
+	// Find the contiguous span from left (inclusive) to right (exclusive)
+	xzm_slice_count_t left, right;
+
+	// Offset of this block relative to the body of the segment
+	const size_t block = _xzm_segment_offset(NULL, chunk_idx, block_idx,
+			block_size);
+	const size_t block_end = block + block_size - 1;
+
+	// Determine the start of the first slice touched by this block
+	const xzm_slice_count_t first_slice =
+			_xzm_segment_slice_offset_index(NULL, block);
+	// Determine end of last slice touched by this block
+	const xzm_slice_count_t limit_slice =
+			_xzm_segment_slice_offset_index(NULL, block_end) + 1;
+
+	// Round the slice boundaries down to their corresponding blocks
+	const xzm_block_index_t first_block =
+			_xzm_segment_offset_chunk_block_index_of(NULL, chunk_idx, block_size,
+			first_slice * XZM_SEGMENT_SLICE_SIZE);
+	xzm_block_index_t last_block =
+			_xzm_segment_offset_chunk_block_index_of(NULL, chunk_idx, block_size,
+			limit_slice * XZM_SEGMENT_SLICE_SIZE - 1);
+
+	// If the slices of this chunk are not perfectly divisible by the
+	// block-size, the "partial" chunk at the end of the slice will never
+	// be free because we cannot allocate from it
+	if (last_block == chunk_capacity) {
+		last_block -= 1;
+	}
+
+	// Determine if LHS blocks are free, and include/exclude them from the
+	// madvisable range accordingly
+	if (_xzm_chunk_block_index_range_is_free(NULL, chunk, first_block, block_idx)) {
+		left = first_slice;
+	} else {
+		left = _xzm_segment_slice_offset_index(NULL, block) + 1;
+	}
+	xzm_debug_assert(left >= chunk_idx);
+
+	// Determine if RHS blocks are free, and include/exclude them from the
+	// madvisable range accordingly
+	if (_xzm_chunk_block_index_range_is_free(NULL, chunk, block_idx, last_block)) {
+		right = limit_slice;
+	} else {
+		right = _xzm_segment_slice_offset_index(NULL, block_end);
+	}
+	xzm_debug_assert(right <= chunk_idx + chunk->xzcs_slice_count);
+
+	*slice_idx = left;
+	*num_slices = (left <= right ? right - left : 0);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_chunk_t *
+_xzm_segment_slice_meta_batch_next(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	xzm_segment_t segment = _xzm_segment_for_slice(zone, slice);
+	xzm_xzone_slice_metadata_u *metadata =
+			&segment->xzs_slice_metadata[_xzm_slice_index(segment, slice)];
+	return &metadata->xzsm_batch_next;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_slice_meta_is_batch_pointer(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+#if CONFIG_XZM_DEFERRED_RECLAIM
+	if ((mach_vm_reclaim_id_t)slice == VM_RECLAIM_ID_NULL) {
+		return false;
+	}
+#endif // CONFIG_XZM_DEFERRED_RECLAIM
+	const uintptr_t slice_addr = (uintptr_t)slice;
+	xzm_segment_t segment = _xzm_segment_for_slice(zone, slice);
+	return !slice || (slice_addr >= (uintptr_t)(segment->xzs_slices) &&
+			slice_addr < (uintptr_t)(segment->xzs_slices + segment->xzs_slice_entry_count));
+}
+
+#if CONFIG_XZM_DEFERRED_RECLAIM
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static mach_vm_reclaim_id_t *
+_xzm_segment_slice_meta_reclaim_id(xzm_segment_t segment,
+		xzm_slice_t slice)
+{
+	xzm_xzone_slice_metadata_u *metadata =
+			&segment->xzs_slice_metadata[_xzm_slice_index(segment, slice)];
+	return &metadata->xzsm_reclaim_id;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static mach_vm_reclaim_id_t *
+_xzm_slice_meta_reclaim_id(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	xzm_segment_t segment = _xzm_segment_for_slice(zone, slice);
+	return _xzm_segment_slice_meta_reclaim_id(segment, slice);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_segment_slice_is_deferred(xzm_segment_t segment, xzm_slice_t slice)
+{
+	mach_vm_reclaim_id_t *reclaim_index = _xzm_segment_slice_meta_reclaim_id(
+			segment, slice);
+	return (*reclaim_index != VM_RECLAIM_ID_NULL);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_slice_is_deferred(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	xzm_segment_t segment = _xzm_segment_for_slice(zone, slice);
+	return _xzm_segment_slice_is_deferred(segment, slice);
+}
+
+#endif // CONFIG_XZM_DEFERRED_RECLAIM
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_slice_kind_is_free_span(xzm_slice_kind_t kind)
+{
+	return (kind == XZM_SLICE_KIND_SINGLE_FREE ||
+			kind == XZM_SLICE_KIND_MULTI_FREE);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uint32_t
+_xzm_xzone_slice_free_mask(xzm_malloc_zone_t zone, xzm_slice_t slice)
+{
+	xzm_chunk_t chunk = _xzm_span_slice_first(slice);
+	xzm_debug_assert(chunk->xzc_bits.xzcb_kind == XZM_SLICE_KIND_SMALL_CHUNK);
+	xzm_xzone_t xz = &zone->xzz_xzones[chunk->xzc_xzone_idx];
+
+	uintptr_t slice_start = (uintptr_t)_xzm_slice_start(zone, slice);
+	uintptr_t slice_end = slice_start + XZM_SEGMENT_SLICE_SIZE - 1;
+
+	// If the slices of this chunk are not perfectly divisible by the
+	// block-size, there will be a "partial" block at the end of the chunk
+	// needing special consideration
+	xzm_block_index_t first = _xzm_chunk_block_index_of_ptr(zone, chunk,
+			slice_start);
+	if (first == xz->xz_chunk_capacity) {
+		// This slice corresponds to the partial block, nothing can be allocated from it
+		return 0u;
+	}
+	xzm_block_index_t last = _xzm_chunk_block_index_of_ptr(zone, chunk,
+			slice_end);
+	if (last == xz->xz_chunk_capacity) {
+		// The partial chunk resides at the end of this slice, exclude it from
+		// the mask
+		last--;
+	}
+
+	xzm_block_index_t span = (last - first) + 1;
+	xzm_debug_assert(first <= last);
+
+	return (uint32_t)(((1ull << span) - 1) << first);
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static uintptr_t
+_xzm_introspect_rebase(uintptr_t orig_base, void *new_base, size_t size,
+		void *ptr, size_t ptr_size)
+{
+	if ((uintptr_t)ptr < orig_base) {
+		return 0;
+	}
+
+	uintptr_t offset = (uintptr_t)ptr - orig_base;
+	uintptr_t offset_end;
+	if (os_add_overflow(offset, ptr_size, &offset_end)) {
+		return 0;
+	}
+	if (offset_end > size) {
+		return 0;
+	}
+
+	return (uintptr_t)new_base + offset;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_xzone_allocation_slot_t
+_xzm_xzone_allocation_slot_for_index(xzm_malloc_zone_t zone, xzm_xzone_t xz,
+		xzm_allocation_index_t alloc_idx)
+{
+	xzm_xzone_index_t xz_idx = xz->xz_idx;
+	size_t alloc_base_idx = alloc_idx * zone->xzz_xzone_count;
+	xzm_debug_assert(alloc_base_idx + xz_idx <
+			zone->xzz_slot_count * zone->xzz_xzone_count);
+	return &zone->xzz_xzone_allocation_slots[alloc_base_idx + xz_idx];
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static const char *
+_xzm_slot_config_to_string(xzm_slot_config_t slot_config)
+{
+	switch(slot_config) {
+	case XZM_SLOT_SINGLE:
+		return "SINGLE";
+	case XZM_SLOT_CLUSTER:
+		return "CLUSTER";
+	case XZM_SLOT_CPU:
+		return "CPU";
+	case XZM_SLOT_LAST:
+	default:
+		xzm_debug_abort("unexpected slot config");
+		return "UNKNOWN";
+	}
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_segment_group_has_madvise_workaround(xzm_segment_group_t sg)
+{
+	return sg->xzsg_main_ref->xzmz_madvise_workaround;
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_segment_group_uses_deferred_reclamation(xzm_segment_group_t sg)
+{
+#if CONFIG_XZM_DEFERRED_RECLAIM
+	switch(sg->xzsg_id) {
+	case XZM_SEGMENT_GROUP_DATA:
+	case XZM_SEGMENT_GROUP_POINTER_XZONES:
+		// XXX: There is an implicit assumption that tiny chunks are
+		// always sequestered. If tiny chunks every support recirculation,
+		// they'll be subject to deferred reclaim alongside their small
+		// counterparts once freed back to the segment group
+		return sg->xzsg_main_ref->xzmz_defer_small;
+	case XZM_SEGMENT_GROUP_POINTER_LARGE:
+	case XZM_SEGMENT_GROUP_DATA_LARGE:
+		return sg->xzsg_main_ref->xzmz_defer_large;
+	default:
+		xzm_abort_with_reason("unknown segment group id", sg->xzsg_id);
+	}
+#else // CONFIG_XZM_DEFERRED_RECLAIM
+	return false;
+#endif // CONFIG_XZM_DEFERRED_RECLAIM
+}
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static size_t
+_xzm_segment_group_min_block_size(xzm_segment_group_t sg)
+{
+	// Note: large alignments can force small allocations into segment groups for
+	// bigger allocations, so this query is not always precise (which is
+	// acceptable for our purposes).
+	const size_t small_block_size_min = 16;
+	const size_t large_block_size_min = XZM_SMALL_BLOCK_SIZE_MAX + 1;
+
+	switch (sg->xzsg_id) {
+	case XZM_SEGMENT_GROUP_POINTER_XZONES:
+	case XZM_SEGMENT_GROUP_DATA:
+		return small_block_size_min;
+	case XZM_SEGMENT_GROUP_POINTER_LARGE:
+	case XZM_SEGMENT_GROUP_DATA_LARGE:
+		return large_block_size_min;
+	default:
+		xzm_abort_with_reason("unknown segment group id", sg->xzsg_id);
+	}
+}
+
+#if CONFIG_MTE
+
+// Return whether this zone might contain tagged allocations.  This is used as
+// the fast path check that avoids touching the xzone (xzm_xzone_t).  Note that
+// unlike `xz->xz_tagged` this function does not take the <size,data>
+// characteristics of the allocation into account, so it should only be used
+// when this precision is not required.
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_zone_memtag_enabled(xzm_malloc_zone_t zone)
+{
+	return zone->xzz_memtag_config.enabled;
+}
+
+// Return whether we tag allocations with these <size,data> characteristics
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_zone_memtag_block(xzm_malloc_zone_t zone, size_t block_size, bool data)
+{
+	struct xzm_memtag_config_s *cfg = &(zone->xzz_memtag_config);
+	return cfg->enabled &&
+			(block_size <= cfg->max_block_size) &&
+			(!data || cfg->tag_data);
+}
+
+// Return whether we tag allocations of this size in this segment group
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_segment_group_memtag_block(xzm_segment_group_t sg, size_t block_size)
+{
+	xzm_malloc_zone_t zone = &(sg->xzsg_main_ref->xzmz_base);
+	bool data = _xzm_segment_group_id_is_data(sg->xzsg_id);
+	// Note: the block_size may be less than the min_block_size for the segment
+	// group in the case where alignment is forcing the allocation of a
+	// smaller-than-normal block from the segment group
+	size_t min_block_size = _xzm_segment_group_min_block_size(sg);
+	return block_size >= min_block_size &&
+			_xzm_zone_memtag_block(zone, block_size, data);
+}
+
+// Return whether this segment group might contain tagged allocations
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static bool
+_xzm_segment_group_memtag_enabled(xzm_segment_group_t sg)
+{
+	xzm_malloc_zone_t zone = &(sg->xzsg_main_ref->xzmz_base);
+	bool data = _xzm_segment_group_id_is_data(sg->xzsg_id);
+	size_t min_block_size = _xzm_segment_group_min_block_size(sg);
+	return _xzm_zone_memtag_block(zone, min_block_size, data);
+}
+
+#endif // CONFIG_MTE
+
+#if CONFIG_XZM_THREAD_CACHE
+
+MALLOC_ALWAYS_INLINE MALLOC_INLINE
+static xzm_thread_cache_t
+_xzm_get_thread_cache(void)
+{
+	return _pthread_getspecific_direct(__TSD_MALLOC_XZONE_THREAD_CACHE);
+}
+
+#endif // CONFIG_XZM_THREAD_CACHE
+
+#endif // __has_feature(bounds_safety)
+
+#endif // __XZONE_INLINE_INTERNAL_H__
+
+#endif // CONFIG_XZONE_MALLOC