Files
hakmem/core/box/tls_sll_box.h
Moe Charm (CI) 9b0d746407 Phase 3d-B: TLS Cache Merge - Unified g_tls_sll[] structure (+12-18% expected)
Merge separate g_tls_sll_head[] and g_tls_sll_count[] arrays into unified
TinyTLSSLL struct to improve L1D cache locality. Expected performance gain:
+12-18% from reducing cache line splits (2 loads → 1 load per operation).

Changes:
- core/hakmem_tiny.h: Add TinyTLSSLL type (16B aligned, head+count+pad)
- core/hakmem_tiny.c: Replace separate arrays with g_tls_sll[8]
- core/box/tls_sll_box.h: Update Box API (13 sites) for unified access
- Updated 32+ files: All g_tls_sll_head[i] → g_tls_sll[i].head
- Updated 32+ files: All g_tls_sll_count[i] → g_tls_sll[i].count
- core/hakmem_tiny_integrity.h: Unified canary guards
- core/box/integrity_box.c: Simplified canary validation
- Makefile: Added core/box/tiny_sizeclass_hist_box.o to link

Build:  PASS (10K ops sanity test)
Warnings: Only pre-existing LTO type mismatches (unrelated)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 07:32:30 +09:00

372 lines
12 KiB
C

// tls_sll_box.h - Box TLS-SLL: Single-Linked List API (Unified Box version)
//
// Goal:
// - Single authoritative Box for TLS SLL operations.
// - All next pointer layout is decided by tiny_next_ptr_box.h (Box API).
// - Callers pass BASE pointers only; no local next_offset arithmetic.
// - Compatible with existing ptr_trace PTR_NEXT_* macros (off is logging-only).
//
// Invariants:
// - g_tiny_class_sizes[cls] is TOTAL stride (including 1-byte header when enabled).
// - For HEADER_CLASSIDX != 0, tiny_nextptr.h encodes:
// class 0: next_off = 0
// class 1-6: next_off = 1
// class 7: next_off = 0
// Callers MUST NOT duplicate this logic.
// - TLS SLL stores BASE pointers only.
// - Box provides: push / pop / splice with capacity & integrity checks.
#ifndef TLS_SLL_BOX_H
#define TLS_SLL_BOX_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "../hakmem_tiny_config.h"
#include "../hakmem_build_flags.h"
#include "../tiny_remote.h"
#include "../tiny_region_id.h"
#include "../hakmem_tiny_integrity.h"
#include "../ptr_track.h"
#include "../ptr_trace.h"
#include "../tiny_debug_ring.h"
#include "tiny_next_ptr_box.h"
// Phase 3d-B: Unified TLS SLL (defined in hakmem_tiny.c)
extern __thread TinyTLSSLL g_tls_sll[TINY_NUM_CLASSES];
extern int g_tls_sll_class_mask; // bit i=1 → SLL allowed for class i
// ========== Debug guard ==========
#if !HAKMEM_BUILD_RELEASE
static inline void tls_sll_debug_guard(int class_idx, void* base, const char* where)
{
(void)class_idx;
if ((uintptr_t)base < 4096) {
fprintf(stderr,
"[TLS_SLL_GUARD] %s: suspicious ptr=%p cls=%d\n",
where, base, class_idx);
abort();
}
}
#else
static inline void tls_sll_debug_guard(int class_idx, void* base, const char* where)
{
(void)class_idx; (void)base; (void)where;
}
#endif
// Normalize helper: callers are required to pass BASE already.
// Kept as a no-op for documentation / future hardening.
static inline void* tls_sll_normalize_base(int class_idx, void* node)
{
(void)class_idx;
return node;
}
// ========== Push ==========
//
// Push BASE pointer into TLS SLL for given class.
// Returns true on success, false if capacity full or input invalid.
static inline bool tls_sll_push(int class_idx, void* ptr, uint32_t capacity)
{
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_push");
// Class mask gate (narrow triage): if disallowed, reject push
if (__builtin_expect(((g_tls_sll_class_mask & (1u << class_idx)) == 0), 0)) {
return false;
}
// Capacity semantics:
// - capacity == 0 → disabled (reject)
// - capacity > 1<<20 → treat as "unbounded" sentinel (no limit)
if (capacity == 0) {
return false;
}
const uint32_t kCapacityHardMax = (1u << 20);
const int unlimited = (capacity > kCapacityHardMax);
if (!ptr) {
return false;
}
// Base pointer only (callers must pass BASE; this is a no-op by design).
ptr = tls_sll_normalize_base(class_idx, ptr);
#if !HAKMEM_BUILD_RELEASE
// Minimal range guard before we touch memory.
if (!validate_ptr_range(ptr, "tls_sll_push_base")) {
fprintf(stderr,
"[TLS_SLL_PUSH] FATAL invalid BASE ptr cls=%d base=%p\n",
class_idx, ptr);
abort();
}
#endif
// Capacity check BEFORE any writes.
uint32_t cur = g_tls_sll[class_idx].count;
if (!unlimited && cur >= capacity) {
return false;
}
#if HAKMEM_TINY_HEADER_CLASSIDX
// Header handling for header classes (class != 0,7).
// Safe mode (HAKMEM_TINY_SLL_SAFEHEADER=1): never overwrite header; reject on magic mismatch.
// Default mode: restore expected header.
if (class_idx != 0 && class_idx != 7) {
static int g_sll_safehdr = -1;
static int g_sll_ring_en = -1; // optional ring trace for TLS-SLL anomalies
if (__builtin_expect(g_sll_safehdr == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_SLL_SAFEHEADER");
g_sll_safehdr = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_sll_ring_en == -1, 0)) {
const char* r = getenv("HAKMEM_TINY_SLL_RING");
g_sll_ring_en = (r && *r && *r != '0') ? 1 : 0;
}
uint8_t* b = (uint8_t*)ptr;
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
if (g_sll_safehdr) {
uint8_t got = *b;
if ((got & 0xF0u) != HEADER_MAGIC) {
// Reject push silently (fall back to slow path at caller)
if (__builtin_expect(g_sll_ring_en, 0)) {
// aux encodes: high 8 bits = got, low 8 bits = expected
uintptr_t aux = ((uintptr_t)got << 8) | (uintptr_t)expected;
tiny_debug_ring_record(0x7F10 /*TLS_SLL_REJECT*/, (uint16_t)class_idx, ptr, aux);
}
return false;
}
} else {
PTR_TRACK_TLS_PUSH(ptr, class_idx);
PTR_TRACK_HEADER_WRITE(ptr, expected);
*b = expected;
}
}
#endif
tls_sll_debug_guard(class_idx, ptr, "push");
#if !HAKMEM_BUILD_RELEASE
// Optional double-free detection: scan a bounded prefix of the list.
{
void* scan = g_tls_sll[class_idx].head;
uint32_t scanned = 0;
const uint32_t limit = (g_tls_sll[class_idx].count < 64)
? g_tls_sll[class_idx].count
: 64;
while (scan && scanned < limit) {
if (scan == ptr) {
fprintf(stderr,
"[TLS_SLL_PUSH] FATAL double-free: cls=%d ptr=%p already in SLL\n",
class_idx, ptr);
ptr_trace_dump_now("double_free");
abort();
}
void* next;
PTR_NEXT_READ("tls_sll_scan", class_idx, scan, 0, next);
scan = next;
scanned++;
}
}
#endif
// Link new node to current head via Box API (offset is handled inside tiny_nextptr).
PTR_NEXT_WRITE("tls_push", class_idx, ptr, 0, g_tls_sll[class_idx].head);
g_tls_sll[class_idx].head = ptr;
g_tls_sll[class_idx].count = cur + 1;
return true;
}
// ========== Pop ==========
//
// Pop BASE pointer from TLS SLL.
// Returns true on success and stores BASE into *out.
static inline bool tls_sll_pop(int class_idx, void** out)
{
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_pop");
// Class mask gate: if disallowed, behave as empty
if (__builtin_expect(((g_tls_sll_class_mask & (1u << class_idx)) == 0), 0)) {
return false;
}
atomic_fetch_add(&g_integrity_check_class_bounds, 1);
void* base = g_tls_sll[class_idx].head;
if (!base) {
return false;
}
// Sentinel guard: remote sentinel must never be in TLS SLL.
if (__builtin_expect((uintptr_t)base == TINY_REMOTE_SENTINEL, 0)) {
g_tls_sll[class_idx].head = NULL;
g_tls_sll[class_idx].count = 0;
#if !HAKMEM_BUILD_RELEASE
fprintf(stderr,
"[TLS_SLL_POP] Remote sentinel detected at head; SLL reset (cls=%d)\n",
class_idx);
#endif
{
static int g_sll_ring_en = -1;
if (__builtin_expect(g_sll_ring_en == -1, 0)) {
const char* r = getenv("HAKMEM_TINY_SLL_RING");
g_sll_ring_en = (r && *r && *r != '0') ? 1 : 0;
}
if (__builtin_expect(g_sll_ring_en, 0)) {
tiny_debug_ring_record(0x7F11 /*TLS_SLL_SENTINEL*/, (uint16_t)class_idx, base, 0);
}
}
return false;
}
#if !HAKMEM_BUILD_RELEASE
if (!validate_ptr_range(base, "tls_sll_pop_base")) {
fprintf(stderr,
"[TLS_SLL_POP] FATAL invalid BASE ptr cls=%d base=%p\n",
class_idx, base);
abort();
}
#endif
tls_sll_debug_guard(class_idx, base, "pop");
#if HAKMEM_TINY_HEADER_CLASSIDX
// Header validation for header-classes (class != 0,7).
if (class_idx != 0 && class_idx != 7) {
uint8_t got = *(uint8_t*)base;
uint8_t expect = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
PTR_TRACK_TLS_POP(base, class_idx);
PTR_TRACK_HEADER_READ(base, got);
if (__builtin_expect(got != expect, 0)) {
#if !HAKMEM_BUILD_RELEASE
fprintf(stderr,
"[TLS_SLL_POP] CORRUPTED HEADER cls=%d base=%p got=0x%02x expect=0x%02x\n",
class_idx, base, got, expect);
ptr_trace_dump_now("header_corruption");
abort();
#else
// In release, fail-safe: drop list.
g_tls_sll[class_idx].head = NULL;
g_tls_sll[class_idx].count = 0;
{
static int g_sll_ring_en = -1;
if (__builtin_expect(g_sll_ring_en == -1, 0)) {
const char* r = getenv("HAKMEM_TINY_SLL_RING");
g_sll_ring_en = (r && *r && *r != '0') ? 1 : 0;
}
if (__builtin_expect(g_sll_ring_en, 0)) {
// aux encodes: high 8 bits = got, low 8 bits = expect
uintptr_t aux = ((uintptr_t)got << 8) | (uintptr_t)expect;
tiny_debug_ring_record(0x7F12 /*TLS_SLL_HDR_CORRUPT*/, (uint16_t)class_idx, base, aux);
}
}
return false;
#endif
}
}
#endif
// Read next via Box API.
void* next;
PTR_NEXT_READ("tls_pop", class_idx, base, 0, next);
#if !HAKMEM_BUILD_RELEASE
if (next && !validate_ptr_range(next, "tls_sll_pop_next")) {
fprintf(stderr,
"[TLS_SLL_POP] FATAL invalid next ptr cls=%d base=%p next=%p\n",
class_idx, base, next);
ptr_trace_dump_now("next_corruption");
abort();
}
#endif
g_tls_sll[class_idx].head = next;
if (g_tls_sll[class_idx].count > 0) {
g_tls_sll[class_idx].count--;
}
// Clear next inside popped node to avoid stale-chain issues.
tiny_next_write(class_idx, base, NULL);
*out = base;
return true;
}
// ========== Splice ==========
//
// Splice a pre-linked chain of BASE pointers into TLS SLL head.
// chain_head is BASE; links are via Box API-compatible next layout.
// Returns number of nodes actually moved (<= capacity remaining).
static inline uint32_t tls_sll_splice(int class_idx,
void* chain_head,
uint32_t count,
uint32_t capacity)
{
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_splice");
if (!chain_head || count == 0 || capacity == 0) {
return 0;
}
uint32_t cur = g_tls_sll[class_idx].count;
if (cur >= capacity) {
return 0;
}
uint32_t room = capacity - cur;
uint32_t to_move = (count < room) ? count : room;
// Traverse chain up to to_move, validate, and find tail.
void* tail = chain_head;
uint32_t moved = 1;
tls_sll_debug_guard(class_idx, chain_head, "splice_head");
#if HAKMEM_TINY_HEADER_CLASSIDX
// Restore header defensively on each node we touch.
{
uint8_t* b = (uint8_t*)chain_head;
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
*b = expected;
}
#endif
while (moved < to_move) {
tls_sll_debug_guard(class_idx, tail, "splice_traverse");
void* next;
PTR_NEXT_READ("tls_splice_trav", class_idx, tail, 0, next);
if (!next) {
break;
}
#if HAKMEM_TINY_HEADER_CLASSIDX
{
uint8_t* b = (uint8_t*)next;
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
*b = expected;
}
#endif
tail = next;
moved++;
}
// Link tail to existing head and install new head.
tls_sll_debug_guard(class_idx, tail, "splice_tail");
PTR_NEXT_WRITE("tls_splice_link", class_idx, tail, 0, g_tls_sll[class_idx].head);
g_tls_sll[class_idx].head = chain_head;
g_tls_sll[class_idx].count = cur + moved;
return moved;
}
#endif // TLS_SLL_BOX_H