2025-12-11 03:47:24 +09:00
|
|
|
// smallobject_hotbox_v5.c - SmallObject HotBox v5 Full Implementation (Phase v5-2)
|
2025-12-11 03:09:57 +09:00
|
|
|
//
|
2025-12-11 03:47:24 +09:00
|
|
|
// Phase v5-2: C6-only full implementation with segment-based allocation
|
2025-12-11 03:09:57 +09:00
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdbool.h>
|
2025-12-11 03:47:24 +09:00
|
|
|
#include <string.h>
|
2025-12-11 03:09:57 +09:00
|
|
|
#include "box/smallsegment_v5_box.h"
|
|
|
|
|
#include "box/smallobject_hotbox_v5_box.h"
|
|
|
|
|
#include "box/smallobject_cold_iface_v5.h"
|
|
|
|
|
#include "box/smallobject_v5_env_box.h"
|
2025-12-11 03:47:24 +09:00
|
|
|
#include "tiny_region_id.h" // For tiny_region_id_write_header
|
|
|
|
|
|
|
|
|
|
#ifndef likely
|
|
|
|
|
#define likely(x) __builtin_expect(!!(x), 1)
|
|
|
|
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-12-11 03:09:57 +09:00
|
|
|
// TLS context
|
|
|
|
|
static __thread SmallHeapCtxV5 g_small_heap_ctx_v5;
|
|
|
|
|
|
|
|
|
|
SmallHeapCtxV5* small_heap_ctx_v5(void) {
|
|
|
|
|
return &g_small_heap_ctx_v5;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 03:47:24 +09:00
|
|
|
// Forward declarations for pool v1 fallback
|
|
|
|
|
extern void* hak_pool_try_alloc(size_t size, uintptr_t site_id);
|
|
|
|
|
extern void hak_pool_free(void* ptr, size_t size, uintptr_t site_id);
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Helper: Slow path (refill from partial or cold)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
static SmallPageMetaV5* alloc_slow_v5(SmallHeapCtxV5* ctx, uint32_t class_idx) {
|
|
|
|
|
SmallClassHeapV5* h = &ctx->cls[class_idx];
|
|
|
|
|
SmallPageMetaV5* cur = h->current;
|
|
|
|
|
|
|
|
|
|
// If current exists but is exhausted, move to full list only
|
|
|
|
|
// (exhausted pages are fully allocated, not partially free)
|
|
|
|
|
if (cur && !cur->free_list) {
|
2025-12-11 04:24:20 +09:00
|
|
|
SMALL_PAGE_V5_PUSH_FULL(h, cur);
|
2025-12-11 03:47:24 +09:00
|
|
|
h->current = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to pop from partial list (pages with some free blocks)
|
2025-12-11 04:24:20 +09:00
|
|
|
SmallPageMetaV5* from_partial = SMALL_PAGE_V5_POP_PARTIAL(h);
|
2025-12-11 03:47:24 +09:00
|
|
|
if (from_partial) {
|
|
|
|
|
h->current = from_partial;
|
|
|
|
|
return from_partial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Refill from cold interface (allocates new page)
|
|
|
|
|
SmallPageMetaV5* page = small_cold_v5_refill_page(ctx, class_idx);
|
|
|
|
|
if (!page) return NULL;
|
|
|
|
|
|
|
|
|
|
h->current = page;
|
|
|
|
|
return page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Phase v5-2: Fast alloc (C6-only full implementation)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
void* small_alloc_fast_v5(size_t size, uint32_t class_idx, SmallHeapCtxV5* ctx) {
|
|
|
|
|
(void)size; // Not used in fast path
|
|
|
|
|
|
|
|
|
|
// C6-only check
|
2025-12-11 04:24:20 +09:00
|
|
|
if (unlikely(class_idx != SMALL_HEAP_V5_C6_CLASS_IDX)) {
|
2025-12-11 03:47:24 +09:00
|
|
|
// Fallback to pool v1 for non-C6 classes
|
|
|
|
|
return hak_pool_try_alloc(size, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:24:20 +09:00
|
|
|
SmallClassHeapV5* h = &ctx->cls[SMALL_HEAP_V5_C6_CLASS_IDX];
|
2025-12-11 03:47:24 +09:00
|
|
|
SmallPageMetaV5* page = h->current;
|
|
|
|
|
|
|
|
|
|
// Fast path: Try current page freelist
|
|
|
|
|
if (likely(page && page->free_list)) {
|
|
|
|
|
void* blk = page->free_list;
|
|
|
|
|
void* next = NULL;
|
|
|
|
|
memcpy(&next, blk, sizeof(void*));
|
|
|
|
|
page->free_list = next;
|
|
|
|
|
page->used++;
|
|
|
|
|
// Write header and return USER pointer
|
|
|
|
|
return tiny_region_id_write_header(blk, class_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Slow path: Current exhausted or NULL
|
|
|
|
|
page = alloc_slow_v5(ctx, class_idx);
|
|
|
|
|
if (unlikely(!page || !page->free_list)) {
|
|
|
|
|
// Cold refill failed, fallback to pool v1
|
|
|
|
|
return hak_pool_try_alloc(size, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allocate from newly acquired page
|
|
|
|
|
void* blk = page->free_list;
|
|
|
|
|
void* next = NULL;
|
|
|
|
|
memcpy(&next, blk, sizeof(void*));
|
|
|
|
|
page->free_list = next;
|
|
|
|
|
page->used++;
|
|
|
|
|
|
|
|
|
|
// Write header and return USER pointer
|
|
|
|
|
return tiny_region_id_write_header(blk, class_idx);
|
2025-12-11 03:09:57 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-11 03:47:24 +09:00
|
|
|
// ============================================================================
|
2025-12-11 04:33:16 +09:00
|
|
|
// Helper: Determine page location in heap lists (Phase v5-3)
|
2025-12-11 03:47:24 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
static inline page_loc_t get_page_location(SmallClassHeapV5* h, SmallPageMetaV5* page,
|
|
|
|
|
SmallPageMetaV5** prev_out) {
|
2025-12-11 03:47:24 +09:00
|
|
|
if (prev_out) *prev_out = NULL;
|
2025-12-11 04:33:16 +09:00
|
|
|
if (!h || !page) return LOC_NONE;
|
2025-12-11 03:47:24 +09:00
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Check current (O(1))
|
|
|
|
|
if (h->current == page) {
|
|
|
|
|
return LOC_CURRENT;
|
2025-12-11 03:47:24 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Check partial list (typically 0-1 pages in v5-3)
|
2025-12-11 03:47:24 +09:00
|
|
|
SmallPageMetaV5* prev = NULL;
|
|
|
|
|
for (SmallPageMetaV5* p = h->partial_head; p; prev = p, p = p->next) {
|
2025-12-11 04:33:16 +09:00
|
|
|
if (p == page) {
|
2025-12-11 03:47:24 +09:00
|
|
|
if (prev_out) *prev_out = prev;
|
2025-12-11 04:33:16 +09:00
|
|
|
return LOC_PARTIAL;
|
2025-12-11 03:47:24 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check full list
|
|
|
|
|
prev = NULL;
|
|
|
|
|
for (SmallPageMetaV5* p = h->full_head; p; prev = p, p = p->next) {
|
2025-12-11 04:33:16 +09:00
|
|
|
if (p == page) {
|
2025-12-11 03:47:24 +09:00
|
|
|
if (prev_out) *prev_out = prev;
|
2025-12-11 04:33:16 +09:00
|
|
|
return LOC_FULL;
|
2025-12-11 03:47:24 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
return LOC_NONE;
|
2025-12-11 03:09:57 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-11 03:47:24 +09:00
|
|
|
// ============================================================================
|
2025-12-11 04:33:16 +09:00
|
|
|
// Phase v5-3: Fast free (C6-only O(1) implementation)
|
2025-12-11 03:47:24 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
void small_free_fast_v5(void* ptr, uint32_t class_idx, SmallHeapCtxV5* ctx) {
|
|
|
|
|
if (unlikely(!ptr)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// C6-only check
|
2025-12-11 04:24:20 +09:00
|
|
|
if (unlikely(class_idx != SMALL_HEAP_V5_C6_CLASS_IDX)) {
|
2025-12-11 03:47:24 +09:00
|
|
|
hak_pool_free(ptr, 0, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Phase v5-3: O(1) segment lookup (no list search)
|
2025-12-11 03:47:24 +09:00
|
|
|
SmallPageMetaV5* page = small_segment_v5_page_meta_of(ptr);
|
2025-12-11 04:33:16 +09:00
|
|
|
if (unlikely(!page)) {
|
|
|
|
|
// Not in v5 segment, fallback to pool v1
|
|
|
|
|
hak_pool_free(ptr, 0, 0);
|
|
|
|
|
return;
|
2025-12-11 03:47:24 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
SmallClassHeapV5* h = &ctx->cls[SMALL_HEAP_V5_C6_CLASS_IDX];
|
|
|
|
|
|
|
|
|
|
// Push to freelist (O(1))
|
2025-12-11 03:47:24 +09:00
|
|
|
void* head = page->free_list;
|
|
|
|
|
memcpy(ptr, &head, sizeof(void*));
|
|
|
|
|
page->free_list = ptr;
|
|
|
|
|
if (page->used > 0) {
|
|
|
|
|
page->used--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle empty page (used == 0)
|
|
|
|
|
if (page->used == 0) {
|
2025-12-11 04:33:16 +09:00
|
|
|
// Fast path: if this is current, just keep it
|
|
|
|
|
if (h->current == page) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine location and unlink (rare path)
|
|
|
|
|
SmallPageMetaV5* prev = NULL;
|
|
|
|
|
page_loc_t loc = get_page_location(h, page, &prev);
|
|
|
|
|
if (loc != LOC_NONE && loc != LOC_CURRENT) {
|
2025-12-11 04:24:20 +09:00
|
|
|
SMALL_PAGE_V5_UNLINK(h, loc, prev, page);
|
2025-12-11 03:47:24 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Promote to current if empty
|
2025-12-11 03:47:24 +09:00
|
|
|
if (!h->current) {
|
|
|
|
|
h->current = page;
|
|
|
|
|
page->next = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Try partial (limit 1)
|
2025-12-11 04:24:20 +09:00
|
|
|
if (h->partial_count < SMALL_HEAP_V5_C6_PARTIAL_LIMIT) {
|
|
|
|
|
SMALL_PAGE_V5_PUSH_PARTIAL(h, page);
|
2025-12-11 03:47:24 +09:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Retire to cold
|
2025-12-11 03:47:24 +09:00
|
|
|
small_cold_v5_retire_page(ctx, page);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 04:33:16 +09:00
|
|
|
// Page not empty - handle full→partial transition
|
|
|
|
|
if (h->current != page) {
|
|
|
|
|
SmallPageMetaV5* prev = NULL;
|
|
|
|
|
page_loc_t loc = get_page_location(h, page, &prev);
|
|
|
|
|
if (loc == LOC_FULL && page->free_list) {
|
|
|
|
|
// Move from full to partial
|
2025-12-11 04:24:20 +09:00
|
|
|
SMALL_PAGE_V5_UNLINK(h, loc, prev, page);
|
2025-12-11 04:33:16 +09:00
|
|
|
if (h->partial_count < SMALL_HEAP_V5_C6_PARTIAL_LIMIT) {
|
|
|
|
|
SMALL_PAGE_V5_PUSH_PARTIAL(h, page);
|
|
|
|
|
} else {
|
|
|
|
|
SMALL_PAGE_V5_PUSH_FULL(h, page);
|
|
|
|
|
}
|
|
|
|
|
} else if (!h->current) {
|
|
|
|
|
// No current, promote this
|
|
|
|
|
if (loc != LOC_NONE) {
|
|
|
|
|
SMALL_PAGE_V5_UNLINK(h, loc, prev, page);
|
|
|
|
|
}
|
|
|
|
|
h->current = page;
|
|
|
|
|
page->next = NULL;
|
2025-12-11 03:47:24 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-11 03:09:57 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-11 03:47:24 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
// Helper: C6 block size query
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
uint32_t small_heap_v5_c6_block_size(void) {
|
|
|
|
|
return SMALL_HEAP_V5_C6_BLOCK_SIZE;
|
2025-12-11 03:09:57 +09:00
|
|
|
}
|