Files
hakmem/core/box/tiny_heap_box.h
2025-12-07 22:42:02 +09:00

656 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// tiny_heap_box.h - TinyHeap front (mimalloc 風) の共通 Box
// 役割:
// - クラスごとの TinyHeap コンテキストcurrent/partial/full + freelistを TLS で保持。
// - ホットパス alloc/free は TinyHeap 内で完結させ、ページ枯渇/全 free の境界でのみ
// Superslab/Tier/Warm などの下層 Box に触れる。
// - C7HotBox はこの TinyHeapBox 上のクラス7ラッパとして実装する。
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../hakmem_build_flags.h"
#include "../hakmem_tiny_superslab.h" // SuperSlab, TinySlabMeta, superslab_refill
#include "../hakmem_super_registry.h" // hak_super_lookup
#include "../superslab/superslab_inline.h" // slab_index_for
#include "../tiny_tls.h" // TinyTLSSlab
#include "../tiny_box_geometry.h" // tiny_stride_for_class
#include "../tiny_region_id.h" // tiny_region_id_write_header
#include "tiny_layout_box.h" // tiny_user_offset
#include "tiny_next_ptr_box.h" // tiny_next_read/write
// Forward decls for SuperSlab active counters (definitions in hakmem_tiny_ss_active_box.inc)
void ss_active_add(SuperSlab* ss, uint32_t n);
#ifndef TINY_HEAP_MAX_PAGES_PER_CLASS
#define TINY_HEAP_MAX_PAGES_PER_CLASS 4
#endif
typedef struct tiny_heap_page_t {
void* free_list; // ページ内 free list 先頭 (BASE)
uint16_t used; // 現在の使用数active count
uint16_t capacity; // ページ内の総ブロック数
uint16_t slab_idx; // 対応する slab index
uint16_t _pad;
uint8_t* base; // slab 先頭(データ領域先頭)
TinySlabMeta* meta; // Superslab メタowner/Tier 判定用)
SuperSlab* ss; // 所有する Superslab
struct tiny_heap_page_t* next;
int32_t c7_active_delta; // C7 meta-light 用: total_active_blocks の差分
int32_t c7_used_delta; // C7 meta-light 用: meta->used の差分
} tiny_heap_page_t;
typedef struct tiny_heap_class_t {
tiny_heap_page_t* current_page;
tiny_heap_page_t* partial_pages;
tiny_heap_page_t* full_pages;
tiny_heap_page_t nodes[TINY_HEAP_MAX_PAGES_PER_CLASS];
uint8_t node_in_use[TINY_HEAP_MAX_PAGES_PER_CLASS];
uint16_t stride; // cached block stride for this class
uint16_t _pad;
} tiny_heap_class_t;
typedef struct tiny_heap_ctx_t {
tiny_heap_class_t cls[TINY_NUM_CLASSES];
uint8_t initialized;
} tiny_heap_ctx_t;
// TLS state (定義は core/hakmem_tiny.c)
extern __thread tiny_heap_ctx_t g_tiny_heap_ctx;
extern __thread int g_tiny_heap_ctx_init;
extern __thread TinyTLSSlab g_tls_slabs[TINY_NUM_CLASSES];
static inline int tiny_c7_heap_stats_enabled(void) {
static int g = -1;
if (__builtin_expect(g == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_C7_HEAP_STATS");
g = (e && *e && *e != '0') ? 1 : 0;
}
return g;
}
static inline int tiny_c7_meta_light_enabled(void) {
static int g = -1;
if (__builtin_expect(g == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_C7_META_LIGHT");
g = (e && *e && *e != '0') ? 1 : 0;
}
return g;
}
static inline int tiny_c7_delta_debug_enabled(void) {
static int g = -1;
if (__builtin_expect(g == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_C7_DELTA_DEBUG");
g = (e && *e && *e != '0') ? 1 : 0;
}
return g;
}
typedef struct {
_Atomic uint64_t alloc_fast_current;
_Atomic uint64_t alloc_slow_prepare;
_Atomic uint64_t free_fast_local;
_Atomic uint64_t free_slow_fallback;
_Atomic uint64_t alloc_prepare_fail;
_Atomic uint64_t alloc_fail;
} TinyC7HeapStats;
extern TinyC7HeapStats g_c7_heap_stats;
static inline int tiny_heap_cold_drain_and_free(int class_idx, void* base) {
(void)class_idx;
(void)base;
return 0;
}
static inline tiny_heap_ctx_t* tiny_heap_ctx_for_thread(void) {
tiny_heap_ctx_t* ctx = &g_tiny_heap_ctx;
if (!g_tiny_heap_ctx_init) {
memset(ctx, 0, sizeof(*ctx));
g_tiny_heap_ctx_init = 1;
ctx->initialized = 1;
for (int c = 0; c < TINY_NUM_CLASSES; c++) {
tiny_heap_class_t* hcls = &ctx->cls[c];
hcls->stride = (uint16_t)tiny_stride_for_class(c);
}
}
return ctx;
}
static inline tiny_heap_class_t* tiny_heap_class(tiny_heap_ctx_t* ctx, int class_idx) {
if (!ctx || class_idx < 0 || class_idx >= TINY_NUM_CLASSES) return NULL;
return &ctx->cls[class_idx];
}
static inline size_t tiny_heap_block_stride(int class_idx) {
return tiny_stride_for_class(class_idx);
}
static inline void tiny_heap_page_clear(tiny_heap_page_t* page) {
if (!page) return;
page->free_list = NULL;
page->used = 0;
page->capacity = 0;
page->slab_idx = 0;
page->base = NULL;
page->meta = NULL;
page->ss = NULL;
page->next = NULL;
page->c7_active_delta = 0;
page->c7_used_delta = 0;
}
static inline void tiny_c7_meta_flush_page(tiny_heap_page_t* page) {
if (!page || !tiny_c7_meta_light_enabled()) return;
if (!page->meta || !page->ss) return;
int32_t active_delta = page->c7_active_delta;
int32_t used_delta = page->c7_used_delta;
if (active_delta == 0 && used_delta == 0) {
return;
}
if (used_delta != 0) {
atomic_fetch_add_explicit(&page->meta->used, used_delta, memory_order_relaxed);
page->c7_used_delta = 0;
}
if (active_delta != 0) {
if (active_delta > 0) {
ss_active_add(page->ss, (uint32_t)active_delta);
} else {
int32_t n = -active_delta;
for (int32_t i = 0; i < n; i++) {
ss_active_dec_one(page->ss);
}
}
page->c7_active_delta = 0;
}
}
static inline int tiny_c7_delta_should_flush(tiny_heap_page_t* page) {
if (!page) return 0;
if (!tiny_c7_meta_light_enabled()) return 0;
int32_t ud = page->c7_used_delta;
int32_t ad = page->c7_active_delta;
int32_t abs_ud = (ud >= 0) ? ud : -ud;
int32_t abs_ad = (ad >= 0) ? ad : -ad;
int32_t abs_max = (abs_ud > abs_ad) ? abs_ud : abs_ad;
if (abs_max == 0) {
return 0;
}
uint16_t cap = page->capacity;
int32_t base_th = (cap > 0) ? ((int32_t)cap * 16) : 256;
if (base_th < 256) {
base_th = 256;
}
return abs_max >= base_th;
}
static __attribute__((noinline, unused)) void tiny_c7_heap_debug_dump_deltas(void) {
if (!tiny_c7_meta_light_enabled() || !tiny_c7_delta_debug_enabled()) {
return;
}
tiny_heap_ctx_t* ctx = tiny_heap_ctx_for_thread();
int class_idx = 7;
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!hcls) return;
uint64_t nonzero_pages = 0;
int64_t used_delta_sum = 0;
int64_t active_delta_sum = 0;
for (int i = 0; i < TINY_HEAP_MAX_PAGES_PER_CLASS; i++) {
tiny_heap_page_t* p = &hcls->nodes[i];
if (p->c7_used_delta != 0 || p->c7_active_delta != 0) {
nonzero_pages++;
used_delta_sum += (int64_t)p->c7_used_delta;
active_delta_sum += (int64_t)p->c7_active_delta;
fprintf(stderr,
"[C7_DELTA_PAGE] idx=%d used_delta=%d active_delta=%d used=%u cap=%u ss=%p slab=%u\n",
i,
p->c7_used_delta,
p->c7_active_delta,
(unsigned)p->used,
(unsigned)p->capacity,
(void*)p->ss,
(unsigned)p->slab_idx);
}
}
fprintf(stderr,
"[C7_DELTA_SUMMARY] nonzero_pages=%llu used_delta_sum=%lld active_delta_sum=%lld\n",
(unsigned long long)nonzero_pages,
(long long)used_delta_sum,
(long long)active_delta_sum);
}
static inline tiny_heap_page_t* tiny_heap_class_acquire_node(tiny_heap_class_t* hcls) {
if (!hcls) return NULL;
for (int i = 0; i < TINY_HEAP_MAX_PAGES_PER_CLASS; i++) {
if (hcls->node_in_use[i] == 0) {
hcls->node_in_use[i] = 1;
tiny_heap_page_t* p = &hcls->nodes[i];
tiny_heap_page_clear(p);
return p;
}
}
return NULL;
}
static inline void tiny_heap_class_release_node(tiny_heap_class_t* hcls, tiny_heap_page_t* page) {
if (!hcls || !page) return;
if (tiny_c7_meta_light_enabled()) {
tiny_c7_meta_flush_page(page);
}
intptr_t idx = page - hcls->nodes;
if (idx >= 0 && idx < TINY_HEAP_MAX_PAGES_PER_CLASS) {
hcls->node_in_use[idx] = 0;
tiny_heap_page_clear(page);
}
}
static inline tiny_heap_page_t* tiny_heap_class_find_page(tiny_heap_class_t* hcls, SuperSlab* ss, int slab_idx) {
if (!hcls || !ss || slab_idx < 0) return NULL;
tiny_heap_page_t* lists[] = {hcls->current_page, hcls->partial_pages, hcls->full_pages};
for (size_t li = 0; li < sizeof(lists) / sizeof(lists[0]); li++) {
tiny_heap_page_t* p = lists[li];
while (p) {
if (p->ss == ss && (int)p->slab_idx == slab_idx) {
return p;
}
p = p->next;
}
}
return NULL;
}
static inline tiny_heap_page_t* tiny_heap_attach_page(tiny_heap_ctx_t* ctx,
int class_idx,
SuperSlab* ss,
int slab_idx) {
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!hcls || !ss || slab_idx < 0) return NULL;
TinySlabMeta* meta = &ss->slabs[slab_idx];
if (meta->class_idx != class_idx) {
return NULL;
}
tiny_heap_page_t* page = tiny_heap_class_find_page(hcls, ss, slab_idx);
if (!page) {
page = tiny_heap_class_acquire_node(hcls);
if (!page) return NULL;
page->ss = ss;
page->slab_idx = (uint16_t)slab_idx;
page->meta = meta;
page->base = tiny_slab_base_for(ss, slab_idx);
page->capacity = meta->capacity;
page->used = atomic_load_explicit(&meta->used, memory_order_relaxed);
page->free_list = atomic_load_explicit(&meta->freelist, memory_order_acquire);
page->next = hcls->partial_pages;
hcls->partial_pages = page;
} else {
page->capacity = meta->capacity;
page->used = atomic_load_explicit(&meta->used, memory_order_relaxed);
page->free_list = atomic_load_explicit(&meta->freelist, memory_order_acquire);
if (!page->base) {
page->base = tiny_slab_base_for(ss, slab_idx);
}
}
if (class_idx == 7 && tiny_c7_meta_light_enabled()) {
if (page->capacity > 0 && page->used > page->capacity) {
page->used = page->capacity;
}
page->c7_used_delta = 0;
page->c7_active_delta = 0;
}
return page;
}
static inline tiny_heap_page_t* tiny_heap_take_current(tiny_heap_class_t* hcls) {
if (!hcls) return NULL;
if (hcls->current_page) {
return hcls->current_page;
}
if (hcls->partial_pages) {
tiny_heap_page_t* page = hcls->partial_pages;
hcls->partial_pages = page->next;
page->next = NULL;
hcls->current_page = page;
return page;
}
return NULL;
}
static inline void tiny_heap_class_unlink(tiny_heap_class_t* hcls, tiny_heap_page_t* page) {
if (!hcls || !page) return;
if (hcls->current_page == page) {
hcls->current_page = NULL;
}
tiny_heap_page_t** lists[] = {&hcls->partial_pages, &hcls->full_pages};
for (size_t i = 0; i < sizeof(lists) / sizeof(lists[0]); i++) {
tiny_heap_page_t** head = lists[i];
tiny_heap_page_t* prev = NULL;
tiny_heap_page_t* cur = head ? *head : NULL;
while (cur) {
if (cur == page) {
if (prev) {
prev->next = cur->next;
} else {
*head = cur->next;
}
break;
}
prev = cur;
cur = cur->next;
}
}
}
static inline tiny_heap_page_t* tiny_heap_page_of(tiny_heap_ctx_t* ctx, int class_idx, void* base_ptr) {
if (!ctx || !base_ptr || class_idx < 0 || class_idx >= TINY_NUM_CLASSES) return NULL;
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!hcls) return NULL;
// TLS fast path: TLS slab の範囲内なら lookup を避ける
TinyTLSSlab* tls = &g_tls_slabs[class_idx];
if (tls->ss && tls->meta && tls->slab_base) {
size_t stride = hcls->stride;
if (stride == 0) {
stride = tiny_heap_block_stride(class_idx);
hcls->stride = (uint16_t)stride;
}
uint16_t cap = tls->meta->capacity;
if (cap > 0) {
uint8_t* slab_base = tls->slab_base;
size_t slab_size = stride * (size_t)cap;
uint8_t* base_u8 = (uint8_t*)base_ptr;
if (base_u8 >= slab_base && base_u8 < (slab_base + slab_size)) {
return tiny_heap_attach_page(ctx, class_idx, tls->ss, tls->slab_idx);
}
}
}
// fallback: Superslab lookup
SuperSlab* ss = hak_super_lookup(base_ptr);
if (!ss || ss->magic != SUPERSLAB_MAGIC) {
return NULL;
}
int slab_idx = slab_index_for(ss, base_ptr);
if (slab_idx < 0 || slab_idx >= ss_slabs_capacity(ss)) {
return NULL;
}
return tiny_heap_attach_page(ctx, class_idx, ss, slab_idx);
}
static inline void tiny_heap_page_push_to_full(tiny_heap_class_t* hcls, tiny_heap_page_t* page) {
if (!hcls || !page) return;
page->next = hcls->full_pages;
hcls->full_pages = page;
}
static inline void tiny_heap_page_push_to_partial(tiny_heap_class_t* hcls, tiny_heap_page_t* page) {
if (!hcls || !page) return;
page->next = hcls->partial_pages;
hcls->partial_pages = page;
}
static inline void tiny_heap_page_becomes_empty(tiny_heap_ctx_t* ctx, int class_idx, tiny_heap_page_t* page) {
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!hcls || !page) return;
if (class_idx == 7 && tiny_c7_meta_light_enabled()) {
tiny_c7_meta_flush_page(page);
}
if (page->meta && page->ss) {
ss_partial_publish(class_idx, page->ss);
}
tiny_heap_class_unlink(hcls, page);
tiny_heap_class_release_node(hcls, page);
}
static inline void tiny_heap_page_mark_full(tiny_heap_class_t* hcls, tiny_heap_page_t* page) {
if (!hcls || !page) return;
tiny_heap_class_unlink(hcls, page);
tiny_heap_page_push_to_full(hcls, page);
}
static inline void* tiny_heap_page_pop(tiny_heap_class_t* hcls, int class_idx, tiny_heap_page_t* page) {
if (!hcls || !page || !page->meta || !page->ss || !page->base) return NULL;
const int meta_light = (class_idx == 7 && tiny_c7_meta_light_enabled());
void* block = NULL;
if (page->free_list) {
block = page->free_list;
void* next = tiny_next_read(class_idx, block);
page->free_list = next;
atomic_store_explicit(&page->meta->freelist, next, memory_order_release);
} else if (page->used < page->capacity) {
size_t stride = hcls->stride;
if (stride == 0) {
stride = tiny_heap_block_stride(class_idx);
hcls->stride = (uint16_t)stride;
}
block = (void*)(page->base + ((size_t)page->used * stride));
if (page->meta->carved < page->capacity) {
page->meta->carved++;
}
} else {
return NULL;
}
page->used++;
if (__builtin_expect(meta_light, 0)) {
page->c7_used_delta++;
page->c7_active_delta++;
if (tiny_c7_delta_should_flush(page)) {
tiny_c7_meta_flush_page(page);
}
return tiny_region_id_write_header(block, class_idx);
}
atomic_fetch_add_explicit(&page->meta->used, 1, memory_order_relaxed);
ss_active_add(page->ss, 1u);
return tiny_region_id_write_header(block, class_idx);
}
static inline void tiny_heap_page_push_free(int class_idx, tiny_heap_page_t* page, void* base_ptr) {
if (!page || !base_ptr || !page->meta) return;
tiny_next_write(class_idx, base_ptr, page->free_list);
page->free_list = base_ptr;
atomic_store_explicit(&page->meta->freelist, base_ptr, memory_order_release);
}
static inline void tiny_heap_page_free_local(tiny_heap_ctx_t* ctx,
int class_idx,
tiny_heap_page_t* page,
void* base_ptr) {
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!hcls || !page || !base_ptr) return;
const int stats_on = (class_idx == 7 && tiny_c7_heap_stats_enabled());
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.free_fast_local, 1, memory_order_relaxed);
}
const int meta_light = (class_idx == 7 && tiny_c7_meta_light_enabled());
tiny_heap_page_push_free(class_idx, page, base_ptr);
if (page->used > 0) {
page->used--;
if (!__builtin_expect(meta_light, 0)) {
atomic_fetch_sub_explicit(&page->meta->used, 1, memory_order_relaxed);
ss_active_dec_one(page->ss);
} else {
page->c7_used_delta--;
page->c7_active_delta--;
if (tiny_c7_delta_should_flush(page)) {
tiny_c7_meta_flush_page(page);
}
}
}
if (page->used == 0) {
tiny_heap_page_becomes_empty(ctx, class_idx, page);
} else if (page->used < page->capacity && page->free_list) {
tiny_heap_page_t* old_cur = hcls->current_page;
tiny_heap_class_unlink(hcls, page);
page->next = NULL;
if (class_idx == 7) {
hcls->current_page = page;
if (old_cur && old_cur != page) {
tiny_heap_page_push_to_partial(hcls, old_cur);
}
} else {
if (!hcls->current_page) {
hcls->current_page = page;
} else {
tiny_heap_page_push_to_partial(hcls, page);
}
}
}
}
static inline tiny_heap_page_t* tiny_heap_prepare_page(tiny_heap_ctx_t* ctx, int class_idx) {
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
const int stats_on = (class_idx == 7 && tiny_c7_heap_stats_enabled());
if (!hcls) {
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.alloc_prepare_fail, 1, memory_order_relaxed);
}
return NULL;
}
tiny_heap_page_t* page = tiny_heap_take_current(hcls);
if (page) return page;
TinyTLSSlab* tls = &g_tls_slabs[class_idx];
if (!tls->ss) {
if (superslab_refill(class_idx) == NULL) {
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.alloc_prepare_fail, 1, memory_order_relaxed);
}
return NULL;
}
}
tls = &g_tls_slabs[class_idx]; // superslab_refill で更新されるため再取得
if (!tls->ss || !tls->meta) {
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.alloc_prepare_fail, 1, memory_order_relaxed);
}
return NULL;
}
page = tiny_heap_attach_page(ctx, class_idx, tls->ss, tls->slab_idx);
if (page) {
tiny_heap_class_unlink(hcls, page);
page->next = NULL;
hcls->current_page = page;
}
return page;
}
// class_idx 固定での alloc ホットパス
static inline void* tiny_heap_alloc_slow_from_class(tiny_heap_ctx_t* ctx, int class_idx) {
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!hcls) return NULL;
const int stats_on = (class_idx == 7 && tiny_c7_heap_stats_enabled());
tiny_heap_page_t* page = tiny_heap_prepare_page(ctx, class_idx);
if (!page) {
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.alloc_fail, 1, memory_order_relaxed);
}
return NULL;
}
void* user = tiny_heap_page_pop(hcls, class_idx, page);
if (user && page->used >= page->capacity && page->free_list == NULL) {
tiny_heap_page_mark_full(hcls, page);
}
return user;
}
// class_idx 固定での alloc ホットパス
__attribute__((always_inline)) static inline void* tiny_heap_alloc_class_fast(tiny_heap_ctx_t* ctx,
int class_idx,
size_t size) {
(void)size;
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
tiny_heap_page_t* page = hcls ? hcls->current_page : NULL;
const int stats_on = (class_idx == 7 && tiny_c7_heap_stats_enabled());
if (page) {
if (page->free_list || page->used < page->capacity) {
void* user = tiny_heap_page_pop(hcls, class_idx, page);
if (user) {
if (page->used >= page->capacity && page->free_list == NULL) {
tiny_heap_page_mark_full(hcls, page);
}
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.alloc_fast_current, 1, memory_order_relaxed);
}
return user;
}
}
}
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.alloc_slow_prepare, 1, memory_order_relaxed);
}
return tiny_heap_alloc_slow_from_class(ctx, class_idx);
}
// Superslab/Slab メタが既に分かっている場合の freeGate から渡されるホットパス用)
static inline void tiny_heap_free_class_fast_with_meta(tiny_heap_ctx_t* ctx,
int class_idx,
SuperSlab* ss,
int slab_idx,
void* base) {
tiny_heap_class_t* hcls = tiny_heap_class(ctx, class_idx);
if (!base || !ss || slab_idx < 0 || !hcls) return;
const int stats_on = (class_idx == 7 && tiny_c7_heap_stats_enabled());
tiny_heap_page_t* page = tiny_heap_attach_page(ctx, class_idx, ss, slab_idx);
if (!page) {
if (__builtin_expect(stats_on, 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.free_slow_fallback, 1, memory_order_relaxed);
}
tiny_heap_cold_drain_and_free(class_idx, base);
return;
}
tiny_heap_page_free_local(ctx, class_idx, page, base);
}
// class_idx 固定での free ホットパスptr は USER ポインタ)
static inline void tiny_heap_free_class_fast(tiny_heap_ctx_t* ctx, int class_idx, void* ptr) {
if (!ptr) return;
#if HAKMEM_TINY_HEADERLESS
void* base = ptr;
#else
void* base = (void*)((uint8_t*)ptr - 1);
#endif
SuperSlab* ss = hak_super_lookup(base);
if (!ss || ss->magic != SUPERSLAB_MAGIC) {
if (__builtin_expect((class_idx == 7 && tiny_c7_heap_stats_enabled()), 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.free_slow_fallback, 1, memory_order_relaxed);
}
tiny_heap_cold_drain_and_free(class_idx, base);
return;
}
int slab_idx = slab_index_for(ss, base);
if (slab_idx < 0 || slab_idx >= ss_slabs_capacity(ss)) {
if (__builtin_expect((class_idx == 7 && tiny_c7_heap_stats_enabled()), 0)) {
atomic_fetch_add_explicit(&g_c7_heap_stats.free_slow_fallback, 1, memory_order_relaxed);
}
tiny_heap_cold_drain_and_free(class_idx, base);
return;
}
tiny_heap_free_class_fast_with_meta(ctx, class_idx, ss, slab_idx, base);
}